提问人:Hans 提问时间:11/1/2022 更新时间:11/1/2022 访问量:97
为什么 operator= 和 copy 构造函数在虚拟继承中被区别对待?
Why operator= and copy constructor are treated differently in virtual inheritance?
问:
似乎在虚拟继承中,operator= 和 copy 构造函数的处理方式不同。请考虑以下代码:
#include <iostream>
#include <ostream>
class A {
public:
A(int x) : x(x) {
std::cout << "A is initialized" << std::endl;
}
A(const A& rhs) {
std::cout << "Copy constructor for A" << std::endl;
}
A& operator=(const A& rhs) {
std::cout << "A::operator=()" << std::endl;
return *this;
}
virtual void funcB() = 0;
virtual void funcC() = 0;
int x;
};
class B : virtual public A {
public:
B(int x) {
std::cout << "B is initialized" << std::endl;
}
B(const B& rhs) {
std::cout << "Copy constructor for B" << std::endl;
}
B& operator=(const B& rhs) {
std::cout << "B::operator=()" << std::endl;
return *this;
}
void funcB() override {
std::cout << "B" << std::endl;
}
void funcC() override = 0;
};
class C : public B {
public:
C(int x) : A(x + 1), B(x) {
std::cout << "C is initialized" << std::endl;
}
void funcC() override {
std::cout << "C" << std::endl;
}
};
int main() {
C c(1);
C c2(c);
c2 = c;
std::cout << c.x;
}
这里 B 实际上继承自 A,C 继承自 B。输出为:
A is initialized
B is initialized
C is initialized
Copy constructor for A
Copy constructor for B
B::operator=()
2
我们可以看到 C 的默认复制构造函数已经成功地调用了 B 和 A 的复制构造函数,这就是我想要的。但是默认的 operator= 没有调用 A 的 operator=,这很奇怪。
对此的一个可能的解释是,A 的复制构造函数是由 B 调用的,而不是由 C 调用的。但是,由于我故意将 B 设置为纯虚拟,因此我不必在 B 的复制构造函数中初始化 A,事实上我没有。所以 A 的复制构造函数很可能是从 C 调用的,但我没有证据,因为无论如何 A 都会在 B 之前初始化,无论谁调用它的构造函数。
答:
c2 = c;
在这里,我们调用 operator=,但类 C 没有它(在您的例子中它不会生成),然后我们从基类 B 调用,它有并且它发生了,因为我们可以将 C 转换为 B。这就是您看到此消息 B::operator=() 的原因。
如果我们调用 operator=,这并不意味着它将从基类调用所有 operator=。operator= 是一种与构造函数或析构函数行为不同的方法。如果你想看到 A 类的副本,那么添加 next:
B& operator=(const B& rhs) {
A::operator=(rhs);
std::cout << "B::operator=()" << std::endl;
return *this;
}
因此,如果您没有显式调用 A::operator=,则它不会复制 - 值 x 在初始化后始终相同。
评论
C::operator=(const C&)
您正在使用编译器生成(即 ),它调用它的所有直接基类(和成员)operator=
C::operator=(const C&)
operator=
由于不是 的直接基类,因此不调用。A
C
A::operator=(const A&)
B
如果需要,则应复制,与构造函数不同,您可以实现不更改它(或您想要的任何内容)的赋值A
B
A
另一方面,if 是直接基类A
class C : public B , virtual public A { ... }
然后生成的编译器将调用 的赋值运算符(并且 A
是否被多次赋值并不具体operator=
A
)
评论