Is there a way to turn off default copy constructors?

Mar 9, 2009 at 5:45pm
Hi all,
The default copy constructor just caused me a lot of grief. I had written a template class which used the operator "=" for assignment (I was using both complicated objects and basic data types).

I had forgotten to overload the "=" operator for one of the complicated objects. The Compiler had no problem with that - it maliciously called the copy constructor (issuing no warning whatsoever), which made objects share pointers and so the program crashed after 10 minutes of computation in an impossible to debug situation.

The resulting headbanging lost me two days of work.

How can this be avoided? Is there a way to tell the compiler to never use default copy constructors unless specifically instructed to do so? How do you deal with such problems?

Thanks for your comments!
Last edited on Mar 9, 2009 at 5:54pm
Mar 9, 2009 at 5:49pm
copy ctor as well as the = operator should always be declared for complex objects. If you do not want these to exist, then make them private in the class, and don't give them bodies:

1
2
3
4
5
6
7
8
9
class C
{
public:
 // stuff

private:
 C(const C&);     // do not give these a body
 C& operator = (const C&);
};


Doing this will throw a compiler error if anywhere outside your class attempts to copy the class because they're private. Also, if they don't have a body, then you'll get a linker error if you accidentally try to copy an object inside your class.
Last edited on Mar 9, 2009 at 5:51pm
Mar 9, 2009 at 10:53pm
Alternatively inherit boost::noncopyable (privately) which saves you the typing above and does the same thing.

Better solution: write classes that don't require specialized copy constructors (which means no raw pointers).

Mar 10, 2009 at 11:41am
Thanks both of you!

I just got a compiler warning, more than one copy constructor available! Now the thing is, I *do not* want copy constructors to be *ever* used! What should I do?
1
2
3
4
5
6
7
8
9
10
class Rational
{
public:
  Rational(){};
private:
  //the below operators/constructors are never given a body
void operator=(Rational&);
void operator==(Rational&);
Rational(const Rational&);
};

Sorry for the post, just found my error! anyways, the P.S. is still valid :)


P.S. If I had to choose two things to throw out of c++, I would start with 1) eliminate default copy constructors. 2) I will forbid expressions like a=b=c;. In case you accidentally write if(a=b) instead of if(a==b), the compiler should refuse to compile - it doesn't at the moment since the operator "=" does return a value - a big design flaw in c++.
Last edited on Mar 10, 2009 at 11:44am
Mar 10, 2009 at 12:52pm
1) Ugh. Not possible. Things like:

void foo( string s );

can never be called since the parameter is pushed onto the stack via
copy construction. (You could never pass parameters by value).

2) Turn on compiler warnings; the compiler should be flagging it.

Mar 10, 2009 at 12:55pm
0) no need to overload ==, that has no default behavior. Also you should change operator = to accept const Rational&.

1) It's likely a carry over from C. I'd agree, actually... at least somewhat. Default copy ctors (and default assignment operators) should exist for structs but not for classes. For classes they generally cause more problems than they solve.

2) Lots of people (including myself) would disagree with you on this point. Mixing up = and == is something you only do for so long -- after a while not only do you stop making that mistake, but you can spot immediately when somebody else makes that mistake in their code (and if you do make the mistake in your code -- you can spot it right away by its behavior). I occasionally (but not very often) rely on a=b to return a value, and wouldn't like that functionality removed.

Although... it is a common problem. I think a better solution to address this problem would have been to use the delphi-style assignment operator. a := b; instead of a = b;. Another way to sort of get around this problem (which Java employed) is to only allow conditionals to input strict bools (so if(a=b) would be an error unless 'a' is a bool), but I dislike that approach.

edit -

jsmith: surely string (and every other complex stl class) overloads the copy ctor. There's no way it uses the default.
Last edited on Mar 10, 2009 at 12:57pm
Mar 10, 2009 at 1:03pm
I have to disagree with you on point #1.

98% of the time the default copy constructor works for the classes/structs I write. I only need to write a custom copy constructor for classes in which member-wise copy is not sufficient (ie, for taking 'deep' copies of pointers). Since holding raw pointers in classes/structs presents its own set of challenges (who owns the pointer? what if someone else has a copy of the pointer? memory leaks, etc), a better design is to use std::auto_ptr (which makes the class non-copyable by default) or boost smart pointers (which makes the default copy constructor work just fine).


Mar 10, 2009 at 7:02pm
Well, here are my reasons against default copy constructors. I never use

void foo( std::string s ); - to me it is much better

void foo( std::string& s ); - you are passing only 4 bytes of information this way - I think it's way more efficient. As far as the danger that foo modifies s passed to it - that is why there are private/public/friend identifiers.

I am not saying eliminate automatic copy constructors - just take away their *default* status. Make them available only through *explicit* programmer call (for example, by introducing a new keyword, say "default_copy").

As far as the if(a=b) warning goes: I wrote
1
2
3
std::string tempS; 
/*...*/
if(tempS[0]='+'){}

and the compiler didn't issue a warning.

Finding that mistake (it popped in a really obscure place in my computations) took me 45 minutes. Now, saving 15 seconds at a time by using syntax like if (tempBool=b()), I will need to use it 180 times to make up for my lost time (provided I never make the mistake again!) :)
Last edited on Mar 10, 2009 at 7:04pm
Mar 10, 2009 at 8:33pm
Agreed, but not even

void foo( int x );

is possible, nor is:

string s( "Hello World" );
string foo( s.substr( 3, 5 ) );

no matter how you write it, unless you want the compiler to default construct foo then use the assignment operator to copy the return value.

Perhaps you mean add a keyword like this:

1
2
3
class Foo {
   default_copy;   // means to provide default copy ctor
};

?

I tried this:

1
2
3
4
5
6
7
#include <string>

int main() {
    std::string tempS = "foo";
    if( tempS[0] = '+' ) 
       tempS[ 0 ] = '-';
}


g++ -Wall foo.cpp
foo.cpp: In function `int main()':
foo.cpp:5: warning: suggest parentheses around assignment used as truth value
Mar 10, 2009 at 10:23pm
just did the same test as you - no compiler warning!
Hip hip hoorah for Microsoft! (Visual studio express 2008)
Mar 10, 2009 at 10:58pm
*me rolls eyes at automatic MS bashing*

did you try changing the compiler's warning level? Perhaps you just don't have the warning level set high enough to get that particular warning.
Mar 11, 2009 at 1:09pm
Found the option! Now it warns me! One must set "warning level" to "level 4" (it used to be at "level 3"). The default is level 3 (I never changed it since the original installation).

After compiling with the new warning settings only warnings I got were that I declare/initialize variables but never use them. *smiles delighted* (this on 7k+ lines of code :))

Thanks a lot Disch!

Follow up:

What I needed is warning level 4 with the following two lines of code:

1
2
3
4
#pragma warning(disable:4100)
//warning C4100: non-referenced formal parameter
#pragma warning(disable:4189)
//warning C4189: variable initialized but never used 


Last edited on Mar 11, 2009 at 1:11pm
Topic archived. No new replies allowed.