Copy constructor question

J

Jeroen

Hi guys,

I have a simple question. If I have a class like:

class A {
A();
~A();
A(A& a);
A(int i);

int myvalue;
};

So there's a copy constructor for integers:

A::A(int i) { myvalue = i;}

But if I also want copy constructors for unsigned int, long, float,
double, long double etc, do I have to write them all out? All bodies of
these copy constructors are exactly the same:

A::A(T& t) { myvalue = t;}

Or can templates help me without having to write out all variants?

And what if I have to deal with other classes/types which are not
clearly a 'simple scalar'? For example 'complex'? This needs another
code body because then I need to get the real value (for example):

A::A(complex<double>& c) { myvalue = c.real();}

And of course the complex variable comes in various variants like
double, float, etc...

Thanks for any hints,

Jeroen
 
I

Ian Collins

Jeroen said:
Hi guys,

I have a simple question. If I have a class like:

class A {
A();
~A();
A(A& a);
A(int i);

int myvalue;
};

So there's a copy constructor for integers:

A::A(int i) { myvalue = i;}
No, this isn't a copy constructor, there is only one of those (A(A& a),
which should real be A(cont A& a)). It s a constructor.
But if I also want copy constructors for unsigned int, long, float,
double, long double etc, do I have to write them all out?

No, not if there is an automatic conversion from the type to int. If
there is, the int constructor will be used.
 
R

Ron Natalie

Jeroen said:
Hi guys,

I have a simple question. If I have a class like:

class A {
A();
~A();
A(A& a);
A(int i);

int myvalue;
};
The above class is probably defective in that it has a explicit
copy constructor but no copy-assignment operator. If you have
non-standard copy semantics it needs to be done in both places.
So there's a copy constructor for integers:

A::A(int i) { myvalue = i;}

The term here is NOT copy-constructor. A copy constructor is
called with a cv-qualified type of the same class. What you
have here is a "coverting constructor" that is a constructor
that can be called with one argument (other than the same class)
and is NOT declared explicit.
But if I also want copy constructors for unsigned int, long, float,
double, long double etc, do I have to write them all out?

If all you are going to do is assign to i, why do you think you
need all of these. The single converting constructor from int
would work fine.
A::A(complex<double>& c) { myvalue = c.real();}

Can you say specialization? I knew you could.

typedef <class T> A(const complex<T>& c) { myvalue = c.real(); }

You should really make your stuff const correct.
 
J

Jeroen

Ian Collins schreef:
No, this isn't a copy constructor, there is only one of those (A(A& a),
which should real be A(cont A& a)). It s a constructor.


No, not if there is an automatic conversion from the type to int. If
there is, the int constructor will be used.

OK, thanks for the answer. And you're right (of course), there's only
one copy constructor. Very sloppy of me :)
 
J

Jeroen

Ron Natalie schreef:
The above class is probably defective in that it has a explicit
copy constructor but no copy-assignment operator. If you have
non-standard copy semantics it needs to be done in both places.

Yes, I know. But I tried to leave as much as 'overhead' out (at least
those things that seem not te be important for the question) from the
example.
The term here is NOT copy-constructor. A copy constructor is
called with a cv-qualified type of the same class. What you
have here is a "coverting constructor" that is a constructor
that can be called with one argument (other than the same class)
and is NOT declared explicit.

Right, very sloppy of me. I knew that...
If all you are going to do is assign to i, why do you think you
need all of these. The single converting constructor from int
would work fine.


Can you say specialization? I knew you could.

typedef <class T> A(const complex<T>& c) { myvalue = c.real(); }

You should really make your stuff const correct.

OK, I'll look the 'template specialization' up on the Internet. Thanks
for the example and reply Ron!
 
E

E. Robert Tisdale

Jeroen said:
I have a simple question. If I have a class like:

class A {
A();
~A();
A(A& a);
A(int i);

int myvalue;
};

You made several mistakes.
> cat A.h
#ifndef A_H
#define A_H
#include <iostream>

class A {
private:
int myvalue;
public:
A(void): myvalue(0) { } // default
A(int i): myvalue(i) { } // explicit
A(A& a): myvalue(a.myvalue) { }// copy
~A(void) { myvalue = 0; } // destructor

friend
std::eek:stream& operator <<(std::eek:stream& os, const A& a) {
return os << a.myvalue;
}
};

#endif//A_H
> cat main.cc
#include "A.h"

int main (int argc, char* argv[]) {
A a(13);
std::cout << "a = " << a << std::endl;
return 0;
}
> g++ -Wall -ansi -O2 -o main main.cc
> ./main
a = 13

You should test your code before posting it to this newsgroup.
Your constructors and destructor should be public
so that you can use them.
Data should be private.
You need some sort of access function (operator << in this case)
so that you can use the object.

There are implicit conversions to int
so the compiler will use them if you don't provide
explicit constructors for unsigned int etc.
 
A

anon

E. Robert Tisdale said:
You made several mistakes.

#ifndef A_H
#define A_H
#include <iostream>

class A {
private:
int myvalue;
public:
A(void): myvalue(0) { } // default

This is a C style of declaring/defining functions. C++ does not use void
for empty parameter list.
A(int i): myvalue(i) { } // explicit
A(A& a): myvalue(a.myvalue) { }// copy

Copy constructor is missing const. It should be:
A( const A &a): myvalue( a.myvalue){}
~A(void) { myvalue = 0; } // destructor

Same comment as for the constructor.
BTW why assign 0 to myvalue?
 
V

Victor Bazarov

anon said:
This is a C style of declaring/defining functions. C++ does not use
void for empty parameter list.


Copy constructor is missing const. It should be:
A( const A &a): myvalue( a.myvalue){}


Same comment as for the constructor.
BTW why assign 0 to myvalue?

In addition to those comments, the two c-tors (the default and the
parameterized on 'int') should be made into one (the new default):

A(int i = 0) : myvalue(i) {}

And there is probably no need for the copy c-tor as it's implemented.
The one the compiler will generate is absolutely sufficient. The
same for the destructor -- there is no need in it, apparently.

V
 
J

James Kanze

[...]
In addition to those comments, the two c-tors (the default and the
parameterized on 'int') should be made into one (the new default):
A(int i = 0) : myvalue(i) {}

Both solutions are possible. Which one you prefer is a question
of style and taste; there's certainly no "should" about it.
And there is probably no need for the copy c-tor as it's implemented.
The one the compiler will generate is absolutely sufficient.

Still, most coding guidelines would require an explicit one, to
avoid making it inline.
The
same for the destructor -- there is no need in it, apparently.

Ditto.
 
V

Victor Bazarov

James said:
[...]
In addition to those comments, the two c-tors (the default and the
parameterized on 'int') should be made into one (the new default):
A(int i = 0) : myvalue(i) {}

Both solutions are possible. Which one you prefer is a question
of style and taste; there's certainly no "should" about it.

Why 'no "should"'? "Should" does not exclude "possible". And I
do not agree that minimizing the number of functions is arbitrary.
It's definitely _better_.
Still, most coding guidelines would require an explicit one, to
avoid making it inline.

How would you "avoid maing it inline" by keeping it in the class
definition?

<shrug>

V
 
E

E. Robert Tisdale

James said:
Victor Bazarov wrote:


Still, most coding guidelines would require an explicit one, to
avoid making it inline.

Victor is correct of course.
Actually, I would write

//A(const A& a): myvalue(a.myvalue) { }// copy

just to document the fact that I didn't forget the copy constructor.
 
I

Ian Collins

E. Robert Tisdale said:
Victor is correct of course.
Actually, I would write

//A(const A& a): myvalue(a.myvalue) { }// copy

just to document the fact that I didn't forget the copy constructor.
That's silly, what happens when the class innards change? You have to
maintain the unused constructor.

If anything, write

//A(const A&);// copy
 
J

James Kanze

James said:
[...]
In addition to those comments, the two c-tors (the default and the
parameterized on 'int') should be made into one (the new default):
A(int i = 0) : myvalue(i) {}
Both solutions are possible. Which one you prefer is a question
of style and taste; there's certainly no "should" about it.
Why 'no "should"'? "Should" does not exclude "possible".

At least in the English I'm most familiar with (that of the
midwest USA), should is a polite form of the imperative. You
*should* adopt the style required by the coding guidelines where
you work. You *can* omit defining the copy constructor in such
cases if the coding guidelines (or other considerations) don't
require it.

As a general rule, in the absense of some specific guideline
otherwise, I will omit it in local classes used only within my
implementation, but provide it explicitly in "published"
classes; those on whose interface other classes, written by
other people, depend. But there are exceptions. In both
directions.
And I do not agree that minimizing the number of functions is
arbitrary. It's definitely _better_.

Except that you don't minimize the number of functions. There
are exactly the same number of functions in both cases.
How would you "avoid maing it inline" by keeping it in the class
definition?

Every coding guideline I've seen forbids defining functions in
the class definition.

We are talking about professional programming, aren't we?
Obviously, if it's just for your own personal satisfaction, you
do whatever gives you the most satisfaction.
 
J

James Kanze

That's silly, what happens when the class innards change? You have to
maintain the unused constructor.
If anything, write
//A(const A&);// copy

More usually, I will have a documentation section encompassing
all of the constructors (plus assignment, destruction and swap);
in the rare cases where I use the compiler generated version, I
will mention this there; as a general rule, it's probably worth
mentionning whether copy and assignment are supported as part of
the class documentation, rather than at the individual function
level.

And of course, even if you count on the compiler generated
forms, you still have to document them. So letting the compiler
do the generation doesn't really save much work. (And if your
shop uses Doxygen, or something along those lines, how do you
make the compiler generated defaults show up in the generated
documentation?)
 
E

E. Robert Tisdale

Ian said:
That's silly. What happens when the class innards change?
You have to maintain the unused constructor.

Well, the problem with any documentation is that
you must maintain it just like it was code.
If anything, write

//A(const A&);// copy

Which means that you declared but forgot to define the copy constructor?
 
R

Richard Herring

On Apr 20, 11:36 pm, "Victor Bazarov" <[email protected]> wrote:

[... on explicitly writing what the compiler would do anyway]
Except that you don't minimize the number of functions. There
are exactly the same number of functions in both cases.

But there are more *user-written* functions which have to be maintained
when you add more data members. And the compiler is better at keeping
track of those members - in three different functions - than I am.
 
R

Richard Herring

Not explicit without the "explicit" keyword.
In addition to those comments, the two c-tors (the default and the
parameterized on 'int') should be made into one (the new default):

A(int i = 0) : myvalue(i) {}

And there is probably no need for the copy c-tor as it's implemented.
The one the compiler will generate is absolutely sufficient. The
same for the destructor -- there is no need in it, apparently.

V
 
J

James Kanze

[... on explicitly writing what the compiler would do anyway]
Except that you don't minimize the number of functions. There
are exactly the same number of functions in both cases.
But there are more *user-written* functions which have to be maintained
when you add more data members. And the compiler is better at keeping
track of those members - in three different functions - than I am.

Any time you change the data members of the class, you have to
consider those functions, regardless of who wrote them. I don't
see where letting the compiler do it gains anything but less
keystrokes. And you end up with inlined functions, which isn't
necessarily a good idea.
 
V

Victor Bazarov

James said:
[..] I don't
see where letting the compiler do it gains anything but less
keystrokes.

Fewer keystrokes means fewer places where the programmer can screw
up. If you don't see any benefit in that, then nothing we can do
about that, unfortunately.
And you end up with inlined functions, which isn't
necessarily a good idea.

Could you elaborate on that, please?

V
 
R

Richard Herring

[... on explicitly writing what the compiler would do anyway]
And I do not agree that minimizing the number of functions is
arbitrary. It's definitely _better_.
Except that you don't minimize the number of functions. There
are exactly the same number of functions in both cases.
But there are more *user-written* functions which have to be maintained
when you add more data members. And the compiler is better at keeping
track of those members - in three different functions - than I am.

Any time you change the data members of the class, you have to
consider those functions, regardless of who wrote them.

Um. Really what you have to consider is (the semantics of) the _data
members_. If my coding standards insist that if a value class has more
than <some small number> data members, they must all have standard
copy/assign/delete semantics (or be wrapped by something which does),
then I'm assured that the compiler-generated functions will do what's
necessary, and I don't have to look in three additional places to ensure
that all the data members are handled correctly.
I don't
see where letting the compiler do it gains anything but less
keystrokes. And you end up with inlined functions, which isn't
necessarily a good idea.
And it isn't necessarily a bad one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,298
Messages
2,571,540
Members
48,275
Latest member
tetedenuit01

Latest Threads

Top