I have a class A with lots of data members, some of which are constant. All data members have proper copy constructors, so I want to default a copy constructor of my class:
class A
{
public:
A() : a(1) {}
A(const A& op) = default;
private:
// ... Lots of constant and non-constant member data ...
const int a;
};
Then, I want to write a constructor which accepts a reference to A and a value which should initialize one of constant data members:
A(const A& op, const int a_);
Here op should be copied, and a should be initialized with a_ after that or instead of copying. I would like to avoid manual initialization of all data members by delegating to a copy constructor, but how to overwrite my const data member in this case?
For example:
// Illegal: constructor delegation can't be mixed with field initialization.
A(const A& op, const int a_) : A(op), a(a_) {}
// Illegal: attempt to assign constant member.
A(const A& op, const int a_) : A(op) { a = a_; }
// Hack. May lead to UB in some cases.
A(const A& op, const int a_) : A(op)
{
*(const_cast<int*>(&a)) = a_;
// ... or same tricks with memcpy ...
}
Obviously, all of these approaches are evil as they try to initialize a twice.
Another solution is to move all constant data to a base class and write all needed ctors, but it looks to verbose.
Is there a cleaner way to implement A(const A&, int a_)?
Unfortunately, C++ const field initialization is a very special case with specific syntax, so is constructor delegation, and the constructor syntax has no provision to mix them, so there can be no clean and neat solution here (at least for current C++ versions, maybe later...). The best I can imagine is:
class A
{
public:
A() : a(1) {}
A(const A& op):
const_field1(op.const_field1),..., a(op.a) // all const fields here
{ init() };
A(const A& op, int a_):
const_field1(op.const_field1),..., a(a)) // dup line at editor level
private:
void init(void) {
// init non const fields here
}
// ... Lots of constant and non-constant member data ...
const int a;
};
It does not make sense if you have only the copy ctor and one single additional one, but it could ease code maintenability if you have many additional ctors. It is a pity that only non const field setting can be factorized across different ctors with a private method, but C++ standard is like that.
What about a copy constructor with a full initialization list? Since your data is const, you will only be able to assign a value to it using initalization list.
A(const A& op, int a_) :
prop_a(op.prop_a_),
prop_b(op.prop_b_),
// continue for all members ...
a(a_) // except a
{
}
Related
I have te following classes (e.g.) :
class A {
public:
A(void) : i(0) {}
A(int val) : i(val) {}
A(const A& other) : i(other.i) {}
A& operator=(const A& other) {
i = other.i;
return *this;
}
int i;
};
class B : public A {
public:
B(void) : A(), j(0) {};
B(const B& other) : A(other), j(other.j) {}
B(int i, int j) : A(other.i), j(other.j) {}
B& operator=(const B& other) {
A::operator=(other);
j = other.j;
return *this;
}
int j;
};
My question is, given the operator= overload and copy constructor in B, if I wanted to be able to create instances of B out of an already initialized instance of A, or to assign existing instances of A to existing instances of B, would it be necessary to define another copy constructor on B and operator= with the following signature ?:
B(const A& other);
B& operator=(const A& other);
The goal would be to be able to instantiate/assign derived class instances only with the Base class information.
PS : I am working in C++98 and unable to use any newer standard.
Yes, you would have to define something like that.
B(const A& other);
This would allow constructing B out of A. This would also allow assigning A to B by way of implicitly converting A to B and then assigning. So that alone should suffice. But you get an extra copy.
B& operator=(const A& other);
This makes assigning A to B more efficient since you avoid the extra copy of the temporary B. This should also allow assigning things that can be implicitly converted to A like:
B b = 1;
If you don't want that you might have to add some explicit. Did C++98 have explicit? That is so last millenium.
Note: In modern C++ this would be more efficient because of copy elision and because you could use move semantic and perfect forwarding references.
The goal would be to be able to instantiate/assign derived class instances only with the Base class information.
Your goal sounds fishy to me.
Normally in practice, you have two distinct scenarios:
data objects (requiring no inheritance hierarchy)
behavior objects (implementing the same interface, through inheritance).
Data objects (since they have no common base class) implement instantiation and assignment (copy constructors and assignment operators).
Behavior objects (objects within class hierarchies) cannot implement assignment from base class objects in a generic way (what will you do with member variables that exist in the derived class but not in the base class? give them a default value? leave them unchanged?).
That said, you should use a different API than copy constructor and assignment in order to avoid confusion and implicit casts.
That is, if you have both:
B& operator=(const B&);
B& operator=(const A&);
then this is ambiguous when the code assigns from a B instance (as the compiler could call either of them).
You could add a B::copy_common_values(const A&);. This would make it more explicit as well.
In case you are trying to solve an x-y problem, also look up prototype design pattern.
I have a class B with a set of constructors and an assignment operator.
Here it is:
class B
{
public:
B();
B(const string& s);
B(const B& b) { (*this) = b; }
B& operator=(const B & b);
private:
virtual void foo();
// and other private member variables and functions
};
I want to create an inheriting class D that will just override the function foo(), and no other change is required.
But, I want D to have the same set of constructors, including copy constructor and assignment operator as B:
D(const D& d) { (*this) = d; }
D& operator=(const D& d);
Do I have to rewrite all of them in D, or is there a way to use B's constructors and operator? I would especially want to avoid rewriting the assignment operator because it has to access all of B's private member variables.
You can explicitly call constructors and assignment operators:
class Base {
//...
public:
Base(const Base&) { /*...*/ }
Base& operator=(const Base&) { /*...*/ }
};
class Derived : public Base
{
int additional_;
public:
Derived(const Derived& d)
: Base(d) // dispatch to base copy constructor
, additional_(d.additional_)
{
}
Derived& operator=(const Derived& d)
{
Base::operator=(d);
additional_ = d.additional_;
return *this;
}
};
The interesting thing is that this works even if you didn't explicitly define these functions (it then uses the compiler generated functions).
class ImplicitBase {
int value_;
// No operator=() defined
};
class Derived : public ImplicitBase {
const char* name_;
public:
Derived& operator=(const Derived& d)
{
ImplicitBase::operator=(d); // Call compiler generated operator=
name_ = strdup(d.name_);
return *this;
}
};
Short Answer: Yes you will need to repeat the work in D
Long answer:
If your derived class 'D' contains no new member variables then the default versions (generated by the compiler should work just fine). The default Copy constructor will call the parent copy constructor and the default assignment operator will call the parent assignment operator.
But if your class 'D' contains resources then you will need to do some work.
I find your copy constructor a bit strange:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Normally copy constructors chain so that they are copy constructed from the base up. Here because you are calling the assignment operator the copy constructor must call the default constructor to default initialize the object from the bottom up first. Then you go down again using the assignment operator. This seems rather inefficient.
Now if you do an assignment you are copying from the bottom up (or top down) but it seems hard for you to do that and provide a strong exception guarantee. If at any point a resource fails to copy and you throw an exception the object will be in an indeterminate state (which is a bad thing).
Normally I have seen it done the other way around.
The assignment operator is defined in terms of the copy constructor and swap. This is because it makes it easier to provide the strong exception guarantee. I don't think you will be able to provide the strong guarantee by doing it this way around (I could be wrong).
class X
{
// If your class has no resources then use the default version.
// Dynamically allocated memory is a resource.
// If any members have a constructor that throws then you will need to
// write your owen version of these to make it exception safe.
X(X const& copy)
// Do most of the work here in the initializer list
{ /* Do some Work Here */}
X& operator=(X const& copy)
{
X tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(X& s) throws()
{
/* Swap all members */
}
};
Even if you derive a class D from from X this does not affect this pattern.
Admittedly you need to repeat a bit of the work by making explicit calls into the base class, but this is relatively trivial.
class D: public X
{
// Note:
// If D contains no members and only a new version of foo()
// Then the default version of these will work fine.
D(D const& copy)
:X(copy) // Chain X's copy constructor
// Do most of D's work here in the initializer list
{ /* More here */}
D& operator=(D const& copy)
{
D tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(D& s) throws()
{
X::swap(s); // swap the base class members
/* Swap all D members */
}
};
You most likely have a flaw in your design (hint: slicing, entity semantics vs value semantics). Having a full copy/value semantics on an object from a polymorphic hierarchy is often not a need at all. If you want to provide it just in case one may need it later, it means you'll never need it. Make the base class non copyable instead (by inheriting from boost::noncopyable for instance), and that's all.
The only correct solutions when such need really appears are the envelop-letter idiom, or the little framework from the article on Regular Objects by Sean Parent and Alexander Stepanov IIRC. All the other solutions will give you trouble with slicing, and/or the LSP.
On the subject, see also C++CoreReference C.67: C.67: A base class should suppress copying, and provide a virtual clone instead if "copying" is desired.
You will have to redefine all constructors that are not default or copy constructors. You do not need to redefine the copy constructor nor assignment operator as those provided by the compiler (according to the standard) will call all the base's versions:
struct base
{
base() { std::cout << "base()" << std::endl; }
base( base const & ) { std::cout << "base(base const &)" << std::endl; }
base& operator=( base const & ) { std::cout << "base::=" << std::endl; }
};
struct derived : public base
{
// compiler will generate:
// derived() : base() {}
// derived( derived const & d ) : base( d ) {}
// derived& operator=( derived const & rhs ) {
// base::operator=( rhs );
// return *this;
// }
};
int main()
{
derived d1; // will printout base()
derived d2 = d1; // will printout base(base const &)
d2 = d1; // will printout base::=
}
Note that, as sbi noted, if you define any constructor the compiler will not generate the default constructor for you and that includes the copy constructor.
The original code is wrong:
class B
{
public:
B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment
B& operator= (const B& b); // copy assignment
private:
// private member variables and functions
};
In general, you can not define the copy constructor in terms of the copy assignment, because the copy assignment must release the resources and the copy constructor don't !!!
To understand this, consider:
class B
{
public:
B(Other& ot) : ot_p(new Other(ot)) {}
B(const B& b) {ot_p = new Other(*b.ot_p);}
B& operator= (const B& b);
private:
Other* ot_p;
};
To avoid memory leak , the copy assignment first MUST delete the memory pointed by ot_p:
B::B& operator= (const B& b)
{
delete(ot_p); // <-- This line is the difference between copy constructor and assignment.
ot_p = new Other(*b.ot_p);
}
void f(Other& ot, B& b)
{
B b1(ot); // Here b1 is constructed requesting memory with new
b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!!
}
So, copy constructor and copy assignment are different because the former construct and object into an initialized memory and, the later, MUST first release the existing memory before constructing the new object.
If you do what is originally suggested in this article:
B(const B& b){(*this) = b;} // copy constructor
you will be deleting an unexisting memory.
I'm experiencing behavior which I don't understand in a copy constructor of derived class.
class A {
A(const A&);
public:
A() = default;
};
class B : public A {
friend class Factory;
B(const int v) : A(), m_test_val(v) {}
public:
int m_test_val;
B(const B&); // no implementation, just declaration
};
class Factory {
public:
static B create(const int v) {
return B(v);
}
};
int main() {
B b = Factory::create(2);
std::cout << b.m_test_val << '\n';
return 0;
}
The behavior I don't understand is a matter of a working copy constructor B::B(const B&); which, however, does not have any implementation.
When I use B::B(const B&) = default; instead, I get an error saying I'm using deleted function (implicitly deleted because of ill-formation) in the return statement of the Factory::create() function (The A::A(const A&) is private and without implementation on purpose).
And of course, when I use B::B(const B&) = delete;, compiler tells me I use a deleted function.
How is it possible that the copy constructor works with no implementation just with declaration?
Note: The example code is based on a much larger code that behaves the same way, hopefully I didn't leave something out.
The actual copy is elided by the compiler, which is allowed since the copy constructor is accessible. The compiler is of course under no obligation to elide this copy and if it didn't I would expect a linker error not finding the implementation of the copy constructor.
Say I have a class with some const reference member variable and I would like to forbid a certain type of construction. So I would declare the according constructor private. Of course, a constructor must initialise all const reference member variables of the class. Doing so, however, results in odd looking code:
class A {
};
class B {
B(const A& a): host(a) {}
private:
B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
Is there another way to prohibit a certain construction type except than declaring the constructor private? I do not want to let the compiler write a constructor for me.
Simply don't define this:
B():host(A()) {} // This is ugly and not needed !!
That is, the following should do what you want to do:
class B {
B(const A& a): host(a) {}
private:
//B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
The idea is if you've defined a constructor that takes parameter(s), then the default constructor is not generated by the compiler. That means, instances of the above class cannot be default created!
B b1; //error - needs default constructor which doesn't exist!
B b2(a); //ok - only way to create an instance!
C++11 solution
In C++11, you can explicity tell the compiler not to generate a particular constructor as:
struct B
{
B(const A &a) {}
B() = delete; //disable
};
Not only that. There is more to it, as explained below:
Now the interesting part
You can also selectively disable constructor(s) for selected types which makes delete more interesting. Consider this,
struct A
{
A (int) {}
};
Object of this class can be created not only with int argument, but any type which implicitly converts to int. For example,
A a1(10); //ok
A a2('x'); //ok - char can convert to int implicitly
B b;
A a3(b); //ok - assume b provides user-defined conversion to int
Now suppose, for whatever reason, I don't want the users of class A to create objects with char or class B , which fortunately or unfortunately can implicitly convert to int, then you can disable them as:
struct A
{
A(int) {}
A(char) = delete; //disable
A(const B&) = delete; //disable
};
Now here you go:
A a1(10); //ok
A a2('x'); //error
B b;
A a3(b); //error - assume (even if) b provides user-defined conversion to int
Online Demo : http://ideone.com/EQl5R
The error messages are very clear:
prog.cpp:9:5: error: deleted function 'A::A(char)'
prog.cpp:10:5: error: deleted function 'A::A(const B&)'
Just leave it out. As soon as you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
If you want to forbid any construction – ending up with a class that has only static members – you can simply declare the constructor as private, and not define it. Such a class is very rarely useful in C++ (since you cannot create instances of it); the only purpose that I can think of is to implement trait classes:
template <typename T>
struct type_to_color {
static char const* value() { return "blue"; }
private:
type_to_color();
};
template <>
struct type_to_color<int> {
// Integers are red!
static char const* value() { return "red"; }
private:
type_to_color();
}
char const* char_color = type_to_color<char>::value();
char const* int_color = type_to_color<int>::value();
However, this is extremely uncommon: trait classes are abundant in C++ but they never declare their constructors as private, it’s just assumed that everybody knows not to instantiate them.
I'll post the C++11 solution: delete the constructor.
class B {
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
As Konrad Rudolph sayd: as soon you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
Therefore, other options are:
Declare the constructor private (so that you can't inherit from your class), but do not provide a definition:
class B {
public:
B(const A& a): host(a) {}
private:
B(); // not implemented!
const A& host;
};
Or in C++11, as R. Martinho Fernandes says:
class B {
public:
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
I have a class B with a set of constructors and an assignment operator.
Here it is:
class B
{
public:
B();
B(const string& s);
B(const B& b) { (*this) = b; }
B& operator=(const B & b);
private:
virtual void foo();
// and other private member variables and functions
};
I want to create an inheriting class D that will just override the function foo(), and no other change is required.
But, I want D to have the same set of constructors, including copy constructor and assignment operator as B:
D(const D& d) { (*this) = d; }
D& operator=(const D& d);
Do I have to rewrite all of them in D, or is there a way to use B's constructors and operator? I would especially want to avoid rewriting the assignment operator because it has to access all of B's private member variables.
You can explicitly call constructors and assignment operators:
class Base {
//...
public:
Base(const Base&) { /*...*/ }
Base& operator=(const Base&) { /*...*/ }
};
class Derived : public Base
{
int additional_;
public:
Derived(const Derived& d)
: Base(d) // dispatch to base copy constructor
, additional_(d.additional_)
{
}
Derived& operator=(const Derived& d)
{
Base::operator=(d);
additional_ = d.additional_;
return *this;
}
};
The interesting thing is that this works even if you didn't explicitly define these functions (it then uses the compiler generated functions).
class ImplicitBase {
int value_;
// No operator=() defined
};
class Derived : public ImplicitBase {
const char* name_;
public:
Derived& operator=(const Derived& d)
{
ImplicitBase::operator=(d); // Call compiler generated operator=
name_ = strdup(d.name_);
return *this;
}
};
Short Answer: Yes you will need to repeat the work in D
Long answer:
If your derived class 'D' contains no new member variables then the default versions (generated by the compiler should work just fine). The default Copy constructor will call the parent copy constructor and the default assignment operator will call the parent assignment operator.
But if your class 'D' contains resources then you will need to do some work.
I find your copy constructor a bit strange:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Normally copy constructors chain so that they are copy constructed from the base up. Here because you are calling the assignment operator the copy constructor must call the default constructor to default initialize the object from the bottom up first. Then you go down again using the assignment operator. This seems rather inefficient.
Now if you do an assignment you are copying from the bottom up (or top down) but it seems hard for you to do that and provide a strong exception guarantee. If at any point a resource fails to copy and you throw an exception the object will be in an indeterminate state (which is a bad thing).
Normally I have seen it done the other way around.
The assignment operator is defined in terms of the copy constructor and swap. This is because it makes it easier to provide the strong exception guarantee. I don't think you will be able to provide the strong guarantee by doing it this way around (I could be wrong).
class X
{
// If your class has no resources then use the default version.
// Dynamically allocated memory is a resource.
// If any members have a constructor that throws then you will need to
// write your owen version of these to make it exception safe.
X(X const& copy)
// Do most of the work here in the initializer list
{ /* Do some Work Here */}
X& operator=(X const& copy)
{
X tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(X& s) throws()
{
/* Swap all members */
}
};
Even if you derive a class D from from X this does not affect this pattern.
Admittedly you need to repeat a bit of the work by making explicit calls into the base class, but this is relatively trivial.
class D: public X
{
// Note:
// If D contains no members and only a new version of foo()
// Then the default version of these will work fine.
D(D const& copy)
:X(copy) // Chain X's copy constructor
// Do most of D's work here in the initializer list
{ /* More here */}
D& operator=(D const& copy)
{
D tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(D& s) throws()
{
X::swap(s); // swap the base class members
/* Swap all D members */
}
};
You most likely have a flaw in your design (hint: slicing, entity semantics vs value semantics). Having a full copy/value semantics on an object from a polymorphic hierarchy is often not a need at all. If you want to provide it just in case one may need it later, it means you'll never need it. Make the base class non copyable instead (by inheriting from boost::noncopyable for instance), and that's all.
The only correct solutions when such need really appears are the envelop-letter idiom, or the little framework from the article on Regular Objects by Sean Parent and Alexander Stepanov IIRC. All the other solutions will give you trouble with slicing, and/or the LSP.
On the subject, see also C++CoreReference C.67: C.67: A base class should suppress copying, and provide a virtual clone instead if "copying" is desired.
You will have to redefine all constructors that are not default or copy constructors. You do not need to redefine the copy constructor nor assignment operator as those provided by the compiler (according to the standard) will call all the base's versions:
struct base
{
base() { std::cout << "base()" << std::endl; }
base( base const & ) { std::cout << "base(base const &)" << std::endl; }
base& operator=( base const & ) { std::cout << "base::=" << std::endl; }
};
struct derived : public base
{
// compiler will generate:
// derived() : base() {}
// derived( derived const & d ) : base( d ) {}
// derived& operator=( derived const & rhs ) {
// base::operator=( rhs );
// return *this;
// }
};
int main()
{
derived d1; // will printout base()
derived d2 = d1; // will printout base(base const &)
d2 = d1; // will printout base::=
}
Note that, as sbi noted, if you define any constructor the compiler will not generate the default constructor for you and that includes the copy constructor.
The original code is wrong:
class B
{
public:
B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment
B& operator= (const B& b); // copy assignment
private:
// private member variables and functions
};
In general, you can not define the copy constructor in terms of the copy assignment, because the copy assignment must release the resources and the copy constructor don't !!!
To understand this, consider:
class B
{
public:
B(Other& ot) : ot_p(new Other(ot)) {}
B(const B& b) {ot_p = new Other(*b.ot_p);}
B& operator= (const B& b);
private:
Other* ot_p;
};
To avoid memory leak , the copy assignment first MUST delete the memory pointed by ot_p:
B::B& operator= (const B& b)
{
delete(ot_p); // <-- This line is the difference between copy constructor and assignment.
ot_p = new Other(*b.ot_p);
}
void f(Other& ot, B& b)
{
B b1(ot); // Here b1 is constructed requesting memory with new
b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!!
}
So, copy constructor and copy assignment are different because the former construct and object into an initialized memory and, the later, MUST first release the existing memory before constructing the new object.
If you do what is originally suggested in this article:
B(const B& b){(*this) = b;} // copy constructor
you will be deleting an unexisting memory.