CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 14 of 14
  1. #1
    Join Date
    Aug 2006
    Posts
    98

    Constructors when resizing std::vector

    Hello,

    I've come across a discrepancy between running the following code under Linux and Windows. I have a vector of MyClass objects, which I resize to a length of 5. The number of MyClass objects is recorded in the MyClass::num variable, which is incremented every time the MyClass constructor is called.

    I thought that if you resize a vector to a length of k, then it calls the constructor k times. This is the case when compiling with Visual Studio 10 in Windows, which prints out "Number of MyClass objects = 5". However, when compiling with GCC in Ubuntu Linux, it prints out "Number of MyClass objects = 1". Hence, the constructor is only called once, despite the fact that my_vector is of length 5. (In both Windows and Linux, it prints out "Length of my_vector = 5").

    Another observation I have made when compiling in Linux is that supposing I call the my_vector.resize(5) function 3 times, then MyClass::num is set to 3. So, it looks like the MyClass constructor is called each time my_vector is resized, rather than for each element of the vector. In Windows, this does not happen, and MyClass::num does not increment if I call my_vector.resize(5) several times, but it does increment for each element of the vector.

    Ideally I would like my Linux code to run like it does in Windows - of course I could just run the constructor again on each element in the vector - but I would have thought that this is done automatically in my_vector.resize().....

    Any ideas what is going on here?

    Thank you.

    Code:
    #include <iostream>
    #include <vector>
    
    class MyClass
    {
    	public:
    		static int num_objects;
    		MyClass()
    		{
    			num_objects++;
    		};
    };
    
    int MyClass::num_objects = 0;
    
    int main()
    {
    	std::vector<MyClass> my_vector(5);
    	my_vector.resize(5);
    	std::cout << "Number of MyClass objects = " << MyClass::num_objects << std::endl;
            std::cout << "Length of my_vector = " << (int)my_vector.size() << std::endl;
    	return 0;
    }

  2. #2
    Join Date
    Jul 2005
    Location
    Netherlands
    Posts
    2,042

    Re: Constructors when resizing std::vector

    Quote Originally Posted by ejohns85 View Post
    Any ideas what is going on here?
    You are only counting how many times the default constructor is called, not how many objects have been created. What's probably happening is that gcc is creating one MyClass object using the default c'tor and then copies that object to the elements in the vector, thus calling the copy c'tor, not the default c'tor.
    Quote Originally Posted by ejohns85 View Post
    Ideally I would like my Linux code to run like it does in Windows - of course I could just run the constructor again on each element in the vector - but I would have thought that this is done automatically in my_vector.resize().....
    You should avoid developing your code to be dependent on how many copies of an object are made. The reason is that a compiler is free to eliminate such copies. Yours is a good example: both compilers are adhering to the standard, which only mandates that resize appends sz - size() default constructed elements (where sz is the function argument). Whether those elements are default constructed one at a time, or a single object is default constructed and then copied should not matter. It's your job to implement your class in such a way that it doesn't matter.
    Cheers, D Drmmr

    Please put [code][/code] tags around your code to preserve indentation and make it more readable.

    As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky

  3. #3
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Constructors when resizing std::vector

    Quote Originally Posted by ejohns85 View Post
    Hello,

    I've come across a discrepancy between running the following code under Linux and Windows.
    You may come across this discrepancy if you run only Visual C++. As D_Drmmr mentioned, the compiler can eliminate copy construction when optimizing code. If you turned on/off optimizations for Visual C++, you may see your program exhibit different behaviour. As stated, you should never write code that is dependent on the number of copies made, as the code will behave differently.

    Copying is something that only the compiler knows when to do, not the coder. The coder's only responsibility is that if a copy is made, the copy is made correctly and consistently.

    A trick question that is asked is this one: how many times is the copy constructor called in the func() function:
    Code:
    class foo
    {
    };
    
    foo func()
    {
       foo f;
       return f;
    }
    
    int main()
    {
        func();
    }
    The unaware C++ programmer will say "one time on return of func()". That is a wrong answer -- the correct answer is "we don't know, only the compiler knows". The copy could be optimized away, or maybe it isn't optimized away.

    So the bottom line to all of your questions is that you cannot rely on the number of times a copy is made. That is a compiler secret -- just be aware that if a copy is made, the copy is correct and your code doesn't produce side-effects (which yours clearly does).

    Regards,

    Paul McKenzie

  4. #4
    Join Date
    Aug 2006
    Posts
    98

    Re: Constructors when resizing std::vector

    Hi,

    Thanks for the help. My program does not actually rely on the number of times the constructor is called, I just wrote that code to illustrate the idea.

    The issue I was having that brought about my post, is that MyClass actually contains a dynamically-allocated float array. When I subsequently assigned values to this array, it would also assign those values to all the other elements of my_vector - because they are all copies of each other. However, I want each element of my_vector to be independent and have its own array at its own location in memory.

    Is it therefore a common / recommended approach to manually call the constructor on every element in a vector, after it has been resized?

    Thanks.

  5. #5
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Constructors when resizing std::vector

    Quote Originally Posted by ejohns85 View Post
    Hi,

    Thanks for the help. My program does not actually rely on the number of times the constructor is called,
    However, that is the explanation of why your program behaves differently.
    The issue I was having that brought about my post, is that MyClass actually contains a dynamically-allocated float array. When I subsequently assigned values to this array, it would also assign those values to all the other elements of my_vector
    Why not just use vectors? Why do you need to dynamically allocate a float array in addition to using vectors?

    Regards,

    Paul McKenzie

  6. #6
    Join Date
    Feb 2002
    Posts
    4,640

    Re: Constructors when resizing std::vector

    Quote Originally Posted by ejohns85 View Post
    The issue I was having that brought about my post, is that MyClass actually contains a dynamically-allocated float array. When I subsequently assigned values to this array, it would also assign those values to all the other elements of my_vector - because they are all copies of each other. However, I want each element of my_vector to be independent and have its own array at its own location in memory.
    This tells me that you do not have a properly implemented copy constructor, or assignment operator.

    Properly implement these two, and your problem goes away.

    Or, as Paul mentioned, use a vector and forget about it!

    Viggy

  7. #7
    Join Date
    Aug 2006
    Posts
    98

    Re: Constructors when resizing std::vector

    Hi,

    The reason I'm using a mixture of vectors and dynamically-allocated arrays is because my dynamically-allocated arrays are very large, and require a very fast access time. The vectors are short and access time is not critical.

    I haven't actually manually created a copy constructor at all - it's not something I've ever needed to do before. So is it good practice to create your own copy constructor when you create a class? So from what I've learned, with the default copy constructor, the pointer to my dynamically-allocated array would simply be copied over - rather than creating a new dynamically-allocated array, which is what I want. I'll give it a go and see if implementing my own copy constructor solves this.

    Thanks.

  8. #8
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Constructors when resizing std::vector

    Quote Originally Posted by ejohns85 View Post
    Hi,

    The reason I'm using a mixture of vectors and dynamically-allocated arrays is because my dynamically-allocated arrays are very large, and require a very fast access time.
    What makes you believe that vectors have a slow access time? Internally, they are using the very same dynamically-allocated array as what you're doing now.
    So is it good practice to create your own copy constructor when you create a class?
    If the class is not supposed to be copyable, then you should turn off the copying by making the copy constructor and assignment operator private functions without an implementation.

    Otherwise, leaving your particular class open to copying without handling the copies yourself could let bugs and other unexpected things happen. So it is imperative that you handle copying if you don't turn off the copy constructor and copy-assignment operators. If the class has members that already copy correctly, then the default copy constructor is adequate and you don't need to write your own. However in your case that you described, you need to write a user-defined copy constructor and assignment operator.
    So from what I've learned, with the default copy constructor, the pointer to my dynamically-allocated array would simply be copied over - rather than creating a new dynamically-allocated array, which is what I want.
    No. This is absolutely wrong.

    What about the destructor? How are you going to know which copy to destroy if all you have is a pointer value? If you code a copy constructor, you will need a copy-assignment (operator =), and a destructor. This is called the rule of three in C++.
    Code:
    void foo()
    {
       YourClass a;
       YourClass b = a;
    }
    Assume that YourClass has pointers to the float array, and on construction, it is dynamically allocated. What happens when foo() returns? If you wrote a destructor (which you would have to do to make sure there are no memory leaks), how do you know whether you're deleting an a or b object, making sure you delete the pointer value only once? Since a and b have the same pointer value, you now have a double delete error, and possibly have corrupted the heap.

    If copying is a concern, either turn off the copying or implement a reference counted class. You can't blindly use the default copy constructor in your case.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; July 12th, 2011 at 04:47 PM.

  9. #9
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Constructors when resizing std::vector

    Quote Originally Posted by Paul McKenzie View Post
    What makes you believe that vectors have a slow access time? Internally, they are using the very same dynamically-allocated array as what you're doing now.
    Ideally, they would be. However, it's worth noting that Microsoft's default vector implementation actually does some bounds-checking even in optimized release mode, unless you define the _SCL_SECURE=0 preprocessor symbol. (With this symbol, then yes, vectors should be just as fast as dynamic arrays. Faster in some cases.)

    Quote Originally Posted by ejohns85
    So from what I've learned, with the default copy constructor, the pointer to my dynamically-allocated array would simply be copied over - rather than creating a new dynamically-allocated array, which is what I want.
    What about the destructor? How are you going to know which copy to destroy if all you have is a pointer value?
    Code:
    void foo()
    {
       YourClass a;
       YourClass b = a;
    }
    Assume that YourClass has pointers to the float array, and on construction, it is dynamically allocated. What happens when foo() returns? If you wrote a destructor (which you would have to do to make sure there are no memory leaks), how do you know whether you're deleting an a or b object, making sure you delete the pointer value only once? Since a and b have the same pointer value, you now have a double delete error, and possibly have corrupted the heap.

    If copying is a concern, either turn off the copying or implement a reference counted class. You can't blindly use the default copy constructor in your case.
    What Paul neglected to mention here is that your "what I want" is indeed desirable in some cases, because it's much faster than allocating a new array and copying everything over. This is why C++0x introduced move semantics. You can give your class a move constructor and a move assignment operator which simply copy over a pointer (and null out the original pointer at the same time), and those will be used instead of the copy operation when possible.

    Move semantics are supported under VS2010 and recent gcc versions, but not by older compilers.

  10. #10
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Constructors when resizing std::vector

    Quote Originally Posted by Lindley View Post
    Ideally, they would be. However, it's worth noting that Microsoft's default vector implementation actually does some bounds-checking even in optimized release mode, unless you define the _SCL_SECURE=0 preprocessor symbol. (With this symbol, then yes, vectors should be just as fast as dynamic arrays. Faster in some cases.)
    Agreed. From what I remember in VC++ 2010, MS has removed the _SCL_SECURE=0 requirement, and have defaulted to fully optimized release builds.
    What Paul neglected to mention here is that your "what I want" is indeed desirable in some cases, because it's much faster than allocating a new array and copying everything over. This is why C++0x introduced move semantics. You can give your class a move constructor and a move assignment operator which simply copy over a pointer (and null out the original pointer at the same time), and those will be used instead of the copy operation when possible.

    Move semantics are supported under VS2010 and recent gcc versions, but not by older compilers.
    Yes, you can use the new move semantics. But as you stated, only newly compliant compiles will have this feature. Most C++ programemrs are not using C++0x at the present time.

    Regards,

    Paul McKenzie

  11. #11
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Constructors when resizing std::vector

    I find the original question interesting: Apart from the answers already provided above, and if the compiler did not do any optimizations, what ''would'' be the correct behaviour?

    Depends on the standard:

    According to C++99, the signature is:

    Code:
    void std::vector::resize ( size_type sz , T c = T ());
    As you can, see, there is only 1 signature, with the second argument given a default. The behavior is "Resizes the vector, and copy constructs c into any new elements".

    However, if you look at the draft of C++0x, there are two signatures:

    Code:
    void std::vector::resize(size_type sz);
    void std::vector::resize(size_type sz, const T& c);
    This time, they made two different signatures, and the second parameter is not optional anymore.

    This is better on several levels:
    1. Even when you don't use the optional parameter, the calling code still has to create the default value, and pass it to the function. Splitting the methods makes sure the caller doesn't have to do that, and saves on passing 1 argument
    2. When actually using the second argument, you are now passing it by const reference, rather than by value.


    Chances are the first signature just forwards to the second, but you still save the default argument creation centralized.

    ----

    Does this help? Probably not, but the more you know...
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  12. #12
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Constructors when resizing std::vector

    Quote Originally Posted by monarch_dodra
    When actually using the second argument, you are now passing it by const reference, rather than by value.
    I have not checked the text of C++98, but C++03 fixed the parameter to be a const reference, so there is no advantage in that area introduced by C++11 (or should I say C++0A?).
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  13. #13
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Constructors when resizing std::vector

    Must have been in a corrigendum or simething, because this 14882:2003 document I'm looking at is by value. Not really important though.

    According to stroustrup, the Final name off C++0x will probably be "C++0B", as in 0x0b == 11. Clever.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  14. #14
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Constructors when resizing std::vector

    Quote Originally Posted by monarch_dodra
    Must have been in a corrigendum or simething, because this 14882:2003 document I'm looking at is by value. Not really important though.
    My copy is labelled "Second edition 2003-10-15".

    Quote Originally Posted by monarch_dodra
    According to stroustrup, the Final name off C++0x will probably be "C++0B", as in 0x0b == 11. Clever.
    Oh yeah, my hex is hexed.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured