与类的成员复制混淆

Confusion with member copying of class

本文关键字:复制 成员      更新时间:2023-10-16

在学习了c++中的成员复制和赋值操作符之后,看看"类的成员复制",它解释了不能生成默认赋值操作符的条件。我不是很清楚的概念,因为下面的例子我尝试实际工作在g++4.5

#include<iostream>
using namespace std;
class Y{
  int& x;
  const int cx;
public:
  Y(int v1,int v2)
    :x(v1),cx(v2)
  {}
  int getx(){return x;}
  int getcx(){return cx;}
};
int main()
{
  int a = 10;
  Y y1(a,a);
  Y y2 = y1;//assignment
  cout<<y1.getx()<<" "<<y1.getcx();
  return 0;
}

那么我哪里没有得到概念。

请举其他例子(如果可能的话),以便我能更好地理解。

Y y2 = y1;不是赋值。这是一个复制构造函数调用。如果在同一行声明和初始化一个变量,则调用一个单参数构造函数,并将等号的右侧作为参数。Y没有阻止默认复制构造函数被实例化(和调用)。

尝试以下操作:

Y y1(10, 10);
Y y2(11, 11);
y2 = y1;

这应该失败,虽然我现在不能测试它

class Y{
  int& x;
  public:
  Y(int v1,int v2)
    :x(v1),cx(v2)
  {} // v1 ceases to exist from this point
};

x是int类型的引用变量。现在将其初始化为v1,这意味着xv1本身的别名。v1的作用域仅在构造函数中。话虽如此-

 Y y2 = y1;//assignment => Not assignment. It is initialization.

等价于

 Y y2(y1);  // compiler is looking for the overloaded constructor ( ie. copy constructor in this case ).
 class Y{
      public:
       Y ( const Y & other ); // copy constructor
       // ...
 };
#include<iostream>
using namespace std;
class Y{
  int& x;
  const int cx;
public:
  Y(int v1,int v2)
    :x(v1),cx(v2)
  {}
  int getx(){return x;}
  int getcx(){return cx;}
};
int main()
{
  int a = 10;
  Y y1(a,a);
  Y y2 = y1;//NOT assignment. Object is yet to be constructed, so calls copy c'tor
  y2 = y1; // assignment operator is called
  cout<<y1.getx()<<" "<<y1.getcx();
  return 0;
}
/*
D:WorkspacesCodeBlocksTestmain.cpp||In member function 'Y& Y::operator=(const Y&)':|
D:WorkspacesCodeBlocksTestmain.cpp|4|error: non-static reference member 'int& Y::x', can't use default assignment operator|
D:WorkspacesCodeBlocksTestmain.cpp|4|error: non-static const member 'const int Y::cx', can't use default assignment operator|
D:WorkspacesCodeBlocksTestmain.cpp||In function 'int main()':|
D:WorkspacesCodeBlocksTestmain.cpp|20|note: synthesized method 'Y& Y::operator=(const Y&)' first required here |
||=== Build finished: 3 errors, 0 warnings ===|
*/

您的类包含不能默认构造或赋值的成员,即:

    引用
  • 常量

因此,不能为类隐含默认构造函数或赋值操作符。例如,您必须编写自己的构造函数:

class Foo
{
  const int a;
  int     & b;
public:
  Foo(int val, int & modify_me) :
    a(val) ,      // initialize the constant
    b(modify_me)  // bind the reference
  { }
};
很明显,您不能默认构造Foo(即 Foo x; )。同样清楚的是,您不能重新分配类Foo(即 x = y; )的对象,因为您不能重新分配引用或常量。

通过为类提供引用或常量成员,实际上是在类本身上授予引用或常量语义,如果您愿意的话,因此这应该是一个相当直接的逻辑结果。例如,重赋值甚至可能在语义上没有意义,因为你的类应该包含一个常量概念。

但是,请注意,可以创建类的副本:这是因为您可以创建引用的"副本"(即进一步别名)和常量的副本。因此,只需逐个成员应用复制构造函数,就可以隐式地使用复制构造函数。所以你可以说:
int n;
Foo x(15, n);
Foo y(x);
Foo z = x;  // these two are identical!

这导致另外两个对象yzy.a == 15z.a == 15, y.bz.b都是对n的引用。(不要被结尾的两种语法所迷惑;都调用复制构造函数)