存在引用时的赋值运算符和复制构造函数

时间:2011-05-25 06:08:06

标签: c++ assignment-operator default-copy-constructor

我只是使用此代码试验参考文献:

class A
{
};

class B
{
public:
    B(A& a): m_a(a){}

    A& m_a;
};

int main()
{
    A a;
    B b(a);
    B b1 = b;
}

我原以为B b1 = b;都会产生错误。相反,当我使用VS2008编译时,我只是收到警告

  

警告C4512:'B':分配   无法生成运算符

我理解为什么我会收到这个警告。但编译器是否也不应该为B b1 = b;语句生成错误?它就像它生成了复制构造函数但没有生成赋值运算符。这两者本身并不相互联系吗?当另一个无法生成时,为其中一个生成默认实现是否有意义?

5 个答案:

答案 0 :(得分:11)

warning C4512: 'B' : assignment operator could not be generated

问题1:为什么会出现此警告?
引用 只能在创建时初始化一次。您无法在创建后重新分配对另一个相同类型变量的引用,因为Reference只是为其创建的类型变量的别名,并将继续保持这样。试图重新分配它会产生错误 通常,编译器默认情况下会为每个类生成一个隐式位智能赋值运算符,但在这种情况下,由于class B有一个引用作为成员m_a,如果编译器要生成一个隐式赋值运算符,它会打破不能重新分配引用的基本规则。因此,编译器会生成此警告,通知您无法生成隐式赋值运算符。

问题2:但编译器不应该为B b1 = b生成错误;声明呢?
生成的警告与此特定操作完全没有关系 B b1 = b;调用隐式(由@AndreyT正确指出)复制构造函数B::B(const B&)。隐式复制构造函数是类默认生成的成员函数之一。因此没有任何警告或错误。

问题3:它就像生成了复制构造函数但没有生成赋值运算符。这两者本身并不相互联系吗?
不,他们根本没有关系。是编译器生成了一个复制构造函数,但它无法生成一个赋值运算符,原因在于回答上面问题1中指定的原因。这是因为成员引用m_a可以在构造函数本身中初始化。它只是创建时的初始赋值而不是=的赋值。

问题4:当无法生成另一个时,是否仅为其中一个生成默认实现是否有意义?
回答3问题似乎回答了这个问题。

重申代码示例中正在执行的操作:

B b(a);调用转化复制构造函数B::B(A&)
B b1 = b;调用默认的复制构造函数B::B(const B&)

考虑其他情况 如果您有B b1 = a;,则会调用B::B(A&),因此不会再出现错误。

但是,如果B::B(A&)被声明为explicit,则编译器会标记错误,而implicit conversions充当conversion function则不允许编译错误。

检查相同的here

答案 1 :(得分:4)

C ++语言中的构造函数执行初始化,而赋值运算符执行赋值。初始化和分配是完全不同的概念。

可以初始化C ++语言中的引用,这就是为什么编译器为带引用的类生成隐式复制构造函数没有问题。 B b1 = b;语句使用隐式生成的复制构造函数。我不明白为什么你期望它产生错误。

但是,无法分配(重新分配)引用本身,这就是编译器拒绝为具有引用的类生成隐式复制赋值运算符的原因。编译器通过发出警告通知您。如果您实际尝试在程序中使用类B的赋值运算符,则最终会出错。

在这方面,引用的情况与const成员的情况几乎相同:如果某个类具有const成员,编译器将没有问题为此类生成隐式复制构造函数,但会拒绝生成隐式赋值。

答案 2 :(得分:1)

引用只能初始化一次,不能更改。构造函数是有效的,因为它初始化m_a,但副本将重新分配m_a,这是禁止的。

答案 3 :(得分:1)

您的样本不需要赋值运算符。 VC ++只是警告你,如果需要,它将无法生成一个。如果您正在编写库而您忘记预计库的用户可能需要复制B,那么这可能是有用的信息。

如果您不想要警告,可以禁止它(例如,使用#pragma),或者您可以声明私有赋值运算符而不实现它。如果有人试图调用赋值运算符,它将失败并出现链接时错误。

答案 4 :(得分:0)

B b(a);是一个有效的声明。因为将B::B(A&)类型的对象传递给A的构造函数时会调用B

顺便说一句,使用g ++时,没有像你提到的那样生成警告。 (恕我直言,不应该有任何警告,因为B b1= b;调用了默认复制构造函数 B::B(const B&)。)

相关问题