为什么=运算符在没有定义的情况下处理结构?

con*_*roy 54 c++ gcc operators

我们来看一个简单的例子:

struct some_struct {
   std::string str;
   int a, b, c;
}

some_struct abc, abc_copy;
abc.str = "some text";
abc.a = 1;
abc.b = 2;
abc.c = 3;

abc_copy = abc;
Run Code Online (Sandbox Code Playgroud)

然后abc_copy是一个精确复制abc..怎么可能没有定义=操作符

(在处理一些代码时,这让我感到意外..)

Mar*_*ork 85

如果你没有定义这四种方法(C++ 11中有六种),编译器会为你生成它们:

  • 默认构造函数
  • 复制构造函数
  • 分配操作员
  • 析构函数
  • 移动构造函数(C++ 11)
  • 移动分配(C++ 11)

如果你想知道为什么?
它是为了保持与C的向后兼容性(因为C结构可以使用=和声明来复制).但它也使得编写简单的类更容易.有人会争辩说,由于"浅拷贝问题",它会增加问题.我反对的论点是你不应该有一个拥有RAW指针的类.通过使用适当的智能指针,问题就会消失.

默认构造函数(如果未定义其他构造函数)

编译器生成的默认构造函数将调用基类的默认构造函数,然后每个成员默认构造函数(按它们声明的顺序)

析构函数(如果没有定义析构函数)

以声明的相反顺序调用每个成员的析构函数.然后调用基类的析构函数.

复制构造函数(如果未定义复制构造函数)

调用传递src对象的基类复制构造函数.然后使用src对象成员调用每个成员的复制构造函数作为要复制的值.

分配操作员

调用传递src对象的基类赋值运算符.然后使用src对象作为要复制的值调用每个成员上的赋值运算符.

移动构造函数(如果没有定义移动构造函数)

调用传递src对象的基类移动构造函数.然后使用src对象成员调用每个成员的move构造函数作为要移动的值.

移动分配操作员

调用传递src对象的基类移动赋值运算符.然后使用src对象作为要复制的值,在每个成员上调用move赋值运算符.

如果你定义一个这样的类:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;
};
Run Code Online (Sandbox Code Playgroud)

编译器将构建的是:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;

    // Conceptually two different versions of the default constructor are built
    // One is for value-initialization the other for zero-initialization
    // The one used depends on how the object is declared.
    //        some_struct* a = new some_struct;     // value-initialized
    //        some_struct* b = new some_struct();   // zero-initialized
    //        some_struct  c;                       // value-initialized
    //        some_struct  d = some_struct();       // zero-initialized
    // Note: Just because there are conceptually two constructors does not mean
    //       there are actually two built.

    // value-initialize version
    some_struct()
        : some_base()            // value-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it
        // PODS not initialized
        , str2()
   {}

    // zero-initialize version
    some_struct()
        : some_base()            // zero-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it.
        , a(0)
        , b(0)
        , c(0)   // 0 is NULL
        , str2()
        // Initialize all padding to zero
   {}

    some_struct(some_struct const& copy)
        : some_base(copy)
        , str1(copy.str1)
        , a(copy.a)
        , b(copy.b)
        , c(copy.c)
        , str2(copy.str2)
    {}

    some_struct& operator=(some_struct const& copy)
    {
        some_base::operator=(copy);
        str1 = copy.str1;
        a    = copy.a;
        b    = copy.b;
        c    = copy.c;
        str2 = copy.str2;
        return *this;
    }

    ~some_struct()
    {}
    // Note the below is pseudo code
    // Also note member destruction happens after user code.
    // In the compiler generated version the user code is empty
        : ~str2()
        // PODs don't have destructor
        , ~str1()
        , ~some_base();
    // End of destructor here.

    // In C++11 we also have Move constructor and move assignment.
    some_struct(some_struct&& copy)
                    //    ^^^^  Notice the double &&
        : some_base(std::move(copy))
        , str1(std::move(copy.str1))
        , a(std::move(copy.a))
        , b(std::move(copy.b))
        , c(std::move(copy.c))
        , str2(std::move(copy.str2))
    {}

    some_struct& operator=(some_struct&& copy)
                               //    ^^^^  Notice the double &&
    {
        some_base::operator=(std::move(copy));
        str1 = std::move(copy.str1);
        a    = std::move(copy.a);
        b    = std::move(copy.b);
        c    = std::move(copy.c);
        str2 = std::move(copy.str2);
        return *this;
    } 
};
Run Code Online (Sandbox Code Playgroud)


MHa*_*ris 7

在C++中,结构体等同于成员默认为公共而非私有访问的类.

如果没有提供,C++编译器也会自动生成类的以下特殊成员:

  • 默认构造函数 - 没有参数,默认初始化所有内容.
  • 复制构造函数 - 即与类同名的方法,它引用同一个类的另一个对象.复制所有值.
  • 析构函数 - 在对象被销毁时调用.默认情况下什么都不做
  • 赋值运算符 - 将一个结构/类分配给另一个结构/类时调用.这是在上述情况下调用的自动生成的方法.

  • 如果存在 *any* 用户定义的构造函数,则也不提供隐式默认构造函数。 (3认同)

Fer*_*cio 5

为了保持与C的源兼容性,这种行为是必要的.

C不能让您定义/覆盖运算符,因此通常使用=运算符复制结构.

  • 根据K&R(第2版,1988,第127页),它是由ANSI C引入的,但大多数现有的编译器已经支持它. (2认同)