I have a piece of code that looks something like this:
class B
{
private:
const A& obj;
size_t val;
// private - prohibited
B& operator=(const B& other);
public:
B(const A& obj_): obj(obj_)
{
val = 0;
}
};
class C
{
void func()
{
A a1;
B b1(a1);
B b2 = b1; // should throw error?
}
}
Class B has a private assignment operator. Nonetheless assignment in C::func() compiles without errors. But how?
B b2 = b1 does not use the assignment operator, because it's not assignment, it's copy initialization - using constructor, not assignment operator.
See also copy initialization
That's the copy constructor getting called, not the assignment operator.
The reason being is that when you initialize a variable, a constructor (in this case, the copy constructor) gets called.
B b2 = b1; // construction
This is different from assignment
b2 = b1; // b2 is already declared, so assignment
To stop this behavior, just delete your copy constructor.
Related
i have a question about assigning one member of the derived class to another of the same derived class,
Can someone please help me understand why this code is printing 14 and not 34?
i dont understand when b2.nb is getting the value of b1.nb, because in this code:
A& operator=(const A& a){
na = a.na*2;
return *this;
}
there is no assignment of b1.nb to b2.nb
class A{
public:
A(int i){na = i;}
A& operator=(const A& a){
na = a.na*2;
return *this;
}
int na;
};
class B : public A{
public:
B(int i, int j): A(j){nb = i;}
int nb;
};
int main()
{
B b1(1,2);
B b2(3,4);
b2 = b1;
cout<<b2.nb<<b2.na;
}
From cppreference
Implicitly-declared copy assignment operator
If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class.
Your B class never declares a copy assignment operator (i.e. an operator with a signature compatible with B& operator=(const B&)). The fact that A does is irrelevant here. B does not declare one, so C++ generates one. That generated copy assignment operator calls the parent class' assignment operator and then assigns any variables declared in B directly, in your case nb.
Just failed an interview because of this, and I'm still confused:
class A
{
public:
A(int a) : a_(a) {}
// Copy constructor
// Assignment operator
private:
int a_;
};
class B : public A
{
public:
B(int a, int b) : A(a), b_(b) {
}
// Copy constructor
// Assignment operator
private:
int b_;
};
How do you implement the copy constr/assignment op, and why does the constructor of B have to have an A initialized in the initializer list? It doesn't work if it's not in the list.
How do you implement the copy constr/assignment op
Trick question, I think. In this case the best option is to do absolutely nothing. Neither class contains (and owns) any resources requiring more than the default special member functions. Observe the Rule of Zero.
Some style guides recommend explicitly defaulting special member functions. In this case
class A
{
public:
A(int a) : a_(a) {}
A(const A &) = default;
A& operator=(const A &) = default;
private:
int a_;
};
may be appropriate.
why does the constructor of B have to have an A initialized in the initializer list?
All class members and base classes must be fully constructed before entering the body of a constructor.
B(int a, int b)
{ // Entering constructor's body. A must be constructed before we get here.
}
A has no default constructor, so the constructor it does have must be explicitly called in the initializer list to construct the base class before the construction of B can proceed.
Addressing comment
A's copy constructor and assignment operator are trivial:
A(const A & src): a_(src.a_)
{
}
A& operator=(const A & src)
{
a_ = src.a_;
return *this;
}
B is a bit trickier because it also has to make sure A is copied
B(const B & src): A(src), // copy the base class
b_(src.b_)
{
}
B& operator=(const B & src)
{
A::operator=(src); // assign the base class by explicitly calling its
// assignment operator
b_ = src.b_;
return *this;
}
Note: If I were hiring, I'd take the programmer who called me out on the trick question over the programmer who did the extra work and risked an unforced error.
why does the constructor of B have to have an A initialized in the
initializer list?
A only has one constructor which takes an int. B has A as a base class, which means that every B object must first construct an A object. How else are you going to construct that A object with it's required parameter except by using an initialiser list?
error: use of deleted function 'A::A(const A&)'
return tmp;
^~~
Why is the copy constructor called only when there is a virtual destructor in A? How to avoid this?
struct B {};
struct A{
std::unique_ptr<B> x;
virtual ~A() = default;
};
A f() {
A tmp;
return tmp;
}
virtual ~A() = default; is a user declared destructor. Because of that, A no longer has a move constructor. That means return tmp; can't move tmp and since tmp is not copyable, you get a compiler error.
There are two ways you can fix this. You can add a move constructor like
struct A{
std::unique_ptr<B> x;
A() = default; // you have to add this since the move constructor was added
A(A&&) = default; // defaulted move
virtual ~A() = default;
};
or you can create a base class that has the virtual destructor and inherit from that like
struct C {
virtual ~C() = default;
};
struct A : C {
std::unique_ptr<B> x;
};
This works because A no longer has a user declared destructor (Yes, C does but we only care about A) so it will still generate a move constructor in A. The important part of this is that C doesn't have a deleted move constructor, it just doesn't have one period, so trying to move it will cause a copy. That means
C's copy constructor is called in A's implicitly generated move constructor since C(std::move(A_obj_to_move_from)) will copy as long as it doesn't have a deleted move constructor.
This is what I am trying to do:
class A{
public:
A(){/*need to initialize something here*/}
};
int main(){
A a; //OK
a=A(); //not OK
a=A(b); //not OK
///Only A a; needs to be allowed.
return 0;
}
I need to initialize something while preventing object initialization with copy constructor and also prevent assignment to existing object.
Note: It would be good if I can do this without >=C++11.
Since C++11, just delete assignment operator:
class A{
public:
A(){/*need to initialize something here*/}
A& operator =(const A&) = delete;
};
You can achieve your goal by declaring the assignment operator and the copy constructor in the private part, and not defining them.
For example:
class A {
private:
A(A&); // declared, not defined
void operator= (A&); // declared, not defined
public:
A() { //do regular stuff }
}
However, if you are using C++11/C++14, you can use the delete keyword for more explicit case of this:
class A {
public:
A() { //do regular stuff }
A(A&) = delete;
void operator= (A&) = delete;
}
Since the move constructor and the move assignment operators will not be generated if you declare any of the destructor / copy constructor / assignment operator, you don't need to the same thing for them.
You can just = delete; the copy assignment operator:
class A {
// ...
void operator=(A) = delete;
};
Alternatively, if you don't use C++11 you can make the copy assignment private.
You can define the assignment operator as deleted. For example
class A{
public:
A(){/*need to initialize something here*/}
A & operator =( const A & ) = delete;
};
Or you can declare it private.
class A{
public:
A(){/*need to initialize something here*/}
private:
A & operator =( const A & );
};
Help? I really have no idea what's happening here?
Why in line 3 of assignments it calls operator= of A, when its an assignment of B to B?
class A{
public:
A& operator=(const A&){cout << "A assignment" << endl;return *this;}
};
class B:public A{
public:
A& operator=(const A&){cout << "B assignment" << endl;return *this;}
};
int main() {
A a;
B b;
B b2;
a=b; // output: A assignment
b=a; // output: B assignment
b=b2; // output: A assignment WHY??
return 0;
}
There's still a compiler-generated assignment operator in class B (it's overloaded); unlike how this works with constructors, defining one or more assignment operator overloads does not prevent the compiler from generating a copy assignment operator when one is lacking. The compiler generated one calls A::operator=. And it's the better match for an argument of type B.
You've defined an assignment operator in B, but there's also another implicit copy-assignment operator generated by the compiler:
B& B::operator=(B const&);
This is a better match than the one that takes A const& so it is chosen in the assignment b = b2 (since b2 is a B it doesn't require a derived-to-base conversion for the one that takes an A). The implicit copy-assignment operator calls the copy-assignment operator of the base class which you wrote:
B& B::operator=(B const& b) {
A::operator=(b);
return *this;
}
This is why it looks like A::operator=(A const&) is being chosen by the assignment.