C ++编译器'浅'的副本和分配

我正在使用C ++进行面向对象编程的课程。

在我们的文字中说,

如果我们不声明复制构造函数,编译器将插入实现浅拷贝的代码。 如果我们不声明一个赋值操作符,编译器会插入实现浅赋值的代码。

我想知道的是,这是否实际上是真实的,实际调用的暗指编译器机制以及它是如何工作的。

不是关于复制构造函数的问题,而是关于编译器行为。

编辑>更多的上下文

复制文本定义的构造函数:

复制构造函数的定义包含逻辑

  • 在所有非资源实例变量上执行浅拷贝
  • 为每个新资源分配内存
  • 将数据从源资源复制到新创建的资源中,
  • 资源由文本定义

    对象在运行时分配的内存表示该对象类的资源。

    该资源的管理需要额外的逻辑,对于不访问资源的简单类来说是不必要的。 这个额外的逻辑确保正确处理资源,通常称为深度复制和分配。


    更确切地说,编译器定义了一个默认的拷贝构造函数和一个默认的拷贝赋值操作符。 这些将通过简单地调用所有成员变量的复制构造函数来复制/构造新对象。

  • 对于像int s和float s这样的基元,这通常不是问题。
  • 对于指针,虽然。 这是个坏消息! 当第一个对象删除那个指针时会发生什么? 现在另一个对象的指针是无效的!
  • 如果一个成员变量不能被复制(也许你使用std::unique_ptr来解决上述问题),那么默认的复制分配/ ctor将不起作用。 如何复制无法复制的内容? 这将导致编译器错误。
  • 如果您定义自己的拷贝构造函数/赋值运算符,则可以改为“深度拷贝”。 您可以:

  • 创建一个新的对象,而不是复制指针
  • 显式地“浅拷贝”指针
  • 根据你实际需要混合上述两个!
  • 使用复制对象中的默认/自定义值初始化成员变量,而不是复制原始对象中的任何内容。
  • 完全禁用复制
  • 在和继续
  • 正如你所看到的,为什么你想要实现(或明确禁止)你自己的拷贝赋值操作符,拷贝构造函数,移动对象和析构函数有很多原因。 事实上,有一个众所周知的C ++惯用法称为“五法则”(以前称为“三法则”),可以指导您决定何时执行此操作。


    是的,这是真的,它确实被称为浅拷贝。 至于它是如何工作的,可以说你有一个指针变量,并将它分配给另一个指针变量。 这只会复制指针而不是它指向的内容,这是一个浅拷贝。 深层副本将创建一个新指针,并复制第一个指针指向的实际内容。

    像这样的东西:

    int* a = new int[10];
    
    // Shallow copying
    int* b = a;   // Only copies the pointer a, not what it points to
    
    // Deep copying
    int* c = new int[10];
    std::copy(a, a + 10, c);  // Copies the contents pointed to by a
    

    关于指针浅拷贝的问题应该是非常明显的:在上例中,在初始化b之后,你有两个指向同一内存的指针。 如果有的话delete[] a; 那么这两个指针变得无效。 如果两个指针位于某个类的不同对象中,则指针之间不存在真正的连接,而第二个对象不知道第一个对象是否删除了它的内存。


    浅拷贝代码是每个字段的简单分配。 如果:

    class S {
      T f;
    };
    S s1, s2;
    

    s1=s2;这样的分配s1=s2; 相当于以下内容:

    class S {
        T f;
      public:
        S &operator=(const S&s) {
          this->f = s.f; // and such for every field, whatever T is
        }
    };
    S s1, s2;
    s1=s2;
    

    标准草案12.8-8中说明了这一点:

    X类的隐式声明拷贝构造函数的形式为X :: X(const X&)if

    - X的每个直接或虚拟基类B都有一个拷贝构造函数,其第一个参数的类型为const B&或const volatile B&,并且

    - 对于类型为M(或其数组)的X的所有非静态数据成员,每个这样的类类型都有一个拷贝构造函数,其第一个参数的类型为const M&或者const volatile M&.123

    否则,隐式声明的拷贝构造函数将具有X :: X(X&)形式

    12.8-28说:

    非联合类X的隐式定义的复制/移动赋值运算符会执行其子对象的成员复制/移动赋值。 按照它们在类定义中声明的顺序排列。

    链接地址: http://www.djcxy.com/p/79373.html

    上一篇: C++ compiler 'shallow' copies and assignments

    下一篇: Implementing Deep Copy