为什么=操作符在结构上工作而没有被定义?
我们来看一个简单的例子:
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;
那么abc_copy是abc
的精确副本 ..如何不定义=运算符 ?
(这使我感到惊讶,当一些代码工作..)
如果你没有定义这四个方法(在C ++ 11中是六个),编译器会为你生成它们:
- 默认构造函数
- 复制构造函数
- 指派操作员
- 析构函数
- 移动构造函数(C ++ 11)
- 移动分配(C ++ 11)
如果你想知道为什么?
它保持与C的向后兼容性(因为C结构可以使用=和在声明中复制)。 但它也使编写简单的类更容易。 有人会认为,由于“浅拷贝问题”,会增加问题。 我反对的观点是你不应该拥有一个拥有RAW指针的类。 通过使用适当的智能指针,问题消失。
默认构造函数(如果没有定义其他构造函数)
编译器生成的默认构造函数将调用基类的默认构造函数,然后每个成员的默认构造函数(按声明顺序)
析构函数(如果没有定义析构函数)
以相反的声明顺序调用每个成员的析构函数。 然后调用基类的析构函数。
复制构造函数(如果没有定义复制构造函数)
调用传递src对象的基类复制构造函数。 然后使用src对象成员调用每个成员的复制构造函数作为要复制的值。
指派操作员
调用传递src对象的基类赋值运算符。 然后使用src对象调用每个成员的赋值运算符作为要复制的值。
移动构造函数(如果没有定义移动构造函数)
调用传递src对象的基类移动构造函数。 然后使用src对象成员调用每个成员的移动构造函数作为要移动的值。
移动分配操作员
调用通过src对象的基类移动赋值操作符。 然后使用src对象调用每个成员上的移动赋值运算符作为要复制的值。
如果你像这样定义一个类:
struct some_struct: public some_base { std::string str1; int a; float b; char* c; std::string str2; };
编译器将构build的是:
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; } };
在C ++中,结构等同于成员默认为公用而不是私有访问的类。
如果没有提供,C ++编译器也会自动生成一个类的以下特殊成员:
- 默认的构造函数 – 没有参数,默认initalizes的一切。
- 复制构造函数 – 即与该类具有相同名称的方法,它接受对同一类的另一个对象的引用。 复制所有值。
- 析构函数 – 当对象被销毁时调用。 默认情况下什么都不做
- 赋值运算符 – 当一个结构/类被分配给另一个时调用。 这是在上述情况下被调用的自动生成的方法。
为了保持与C的源代码兼容性,这种行为是必要的。
C没有给你定义/重载操作符的能力,所以通常使用=操作符复制结构体。
但它被定义。 在标准中。 如果您不提供操作员=,则提供给您。 而默认的运算符只是复制每个成员variables。 它如何知道复制每个成员的方式? 它调用他们的operator =(如果没有定义的话,默认情况下会提供…)。
赋值运算符 ( operator=
)是C ++中一个结构或类的隐式生成的函数之一。
这里是一个参考,描述了4个隐式生成的成员:
http://www.cs.ucf.edu/~leavens/larchc++manual/lcpp_136.html
简而言之,隐式生成的成员执行成员浅拷贝 。 以下是链接页面的长版本:
隐式生成的赋值运算符规范在需要时如下所示。 规范说结果是被赋值的对象(
self
),并且self
在后状态self
中的抽象值的值与参数的抽象值的值是相同的。
// @(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $ #include "default_interfaces.lh" T& T::operator = (const T& from) throw(); //@ behavior { //@ requires assigned(from, any) /\ assigned(from\any, any); //@ modifies self; //@ ensures result = self /\ self" = from\any\any; //@ ensures redundantly assigned(self, post) /\ assigned(self', post); // thus //@ ensures redundantly assigned(result, post) /\ assigned(result', post); //@ }
编译器会为你合成一些成员,如果你没有明确地定义它们。 赋值运算符就是其中之一。 复制构造函数是另一个,你也得到一个析构函数。 如果你不提供你自己的构造函数,你也会得到一个默认的构造函数。 除此之外,我不确定还有什么,但我相信可能有其他人(280Z28给出的答案中的链接,但是我不记得现在在哪里读,所以也许只有四个)。
结构基本上是它在内存中的组件的连接(有一些可能的填充内置于alignment)。 当您将一个结构赋值为另一个结构时,这些值只是应对了。