马边彝族移动网站建设,青羊区企业网站建设策划,软件开发交付流程,南阳做网站优化价格转载#xff1a;http://blog.csdn.net/hebbely/article/details/65437510 简述#xff1a; C中一般创建对象#xff0c;拷贝或赋值的方式有构造函数#xff0c;拷贝构造函数#xff0c;赋值函数这三种方法。拷贝构造函数使用已有的对象创建一个新的对象#xff0c;赋值运… 转载http://blog.csdn.net/hebbely/article/details/65437510 简述 C中一般创建对象拷贝或赋值的方式有构造函数拷贝构造函数赋值函数这三种方法。拷贝构造函数使用已有的对象创建一个新的对象赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符主要是否有新的对象产生。 1、构造函数 ① 构造函数是一种特殊的类成员函数是当创建一个类的对象时它被调用来对类的数据成员进行初始化和分配内存。构造函数的命名必须和类名完全相同 ② 首先说一下一个C的空类编译器会加入哪些默认的成员函数 ·默认构造函数和拷贝构造函数 ·析构函数 ·赋值函数赋值运算符 ·取值函数 **即使程序没定义任何成员编译器也会插入以上的函数 注意构造函数可以被重载可以多个可以带参数析构函数只有一个不能被重载不带参数 ③ 而默认构造函数没有参数它什么也不做。当没有重载无参构造函数时 Person man就是通过默认构造函数来创建一个对象 ④下面代码为构造函数重载的实现 [cpp] view plain copy class Person { public: Person() { qDebug()”无参构造函数”endl; } Person(int i):m_i(i) {} //初始化列表 private: int m_i; } 2、初步认识拷贝构造函数和赋值运算符 ① 拷贝构造函数是C独有的它是一种特殊的构造函数用基于同一类的一个对象构造和初始化另一个对象。 ② 在默认情况下用户没有定义但是也没有显式的删除编译器会自动的隐式生成一个拷贝构造函数和赋值运算符。但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符这样的对象就不能通过值传递也不能进行赋值运算。 [cpp] view plain copy class Person { public: Person(const Person p) delete; //不生成拷贝构造函数 Person operator(const Person p) delete; //不生成赋值运算符 private: int age; string name; }; 上面的定义的类Person显式的删除了拷贝构造函数和赋值运算符在需要调用拷贝构造函数或者赋值运算符的地方会提示_无法调用该函数它是已删除的函数_。 ③ 还有一点需要注意的是拷贝构造函数必须以引用的方式传递参数。这是因为在值传递的方式传递给一个函数的时候会调用拷贝构造函数生成函数的实参。如果拷贝构造函数的参数仍然是以值的方式就会无限循环的调用下去直到函数的栈溢出。 3、调用场合 ① 拷贝构造函数和赋值运算符的行为比较相似都是将一个对象的值复制给另一个对象但是其结果却有些不同拷贝构造函数使用传入对象的值生成一个新的对象的实例而赋值运算符是将对象的值复制给一个已经存在的实例。这种区别从两者的名字也可以很轻易的分辨出来拷贝构造函数也是一种构造函数那么它的功能就是创建一个新的对象实例赋值运算符是执行某种运算将一个对象的值复制给另一个对象已经存在的。调用的是拷贝构造函数还是赋值运算符主要是看是否有新的对象实例产生。如果产生了新的对象实例那调用的就是拷贝构造函数如果没有那就是对已有的对象赋值调用的是赋值运算符。拷贝构造函数是一个对象初始化一块内存区域这块内存就是新对象的内存区而赋值函数是对于一个已经被初始化的对象来进行赋值操作。 ② 调用拷贝构造函数主要有以下场景 对象作为函数的参数以值传递的方式传给函数。 对象作为函数的返回值以值的方式从函数返回使用一个对象给另一个对象初始化 ③ 什么时候编译器会生成默认的拷贝构造函数 A. 如果用户没有自定义拷贝构造函数并且在代码中使用到了拷贝构造函数编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数编译器就不在生成。 B. 如果用户定义了一个构造函数但不是拷贝构造函数而此时代码中又用到了拷贝构造函数那编译器也会生成默认的拷贝构造函数。 ④ 样例代码如下 [cpp] view plain copy class Person { public: Person(){} Person(const Person p) { cout Copy Constructor endl; } Person operator(const Person p) { cout Assign endl; return *this; } private: int age; string name; }; void f(Person p) { return; } Person f1() { Person p; return p; } int main() { Person p; Person p1 p; // A Person p2; p2 p; // B f(p2); // C p2 f1(); // D Person p3 f1(); // E getchar(); return 0; } 上面代码中定义了一个类Person显式的定义了拷贝构造函数和赋值运算符。然后定义了两个函数:f(),以值的方式参传入Person对象f1(),以值的方式返回Person对象。在main中模拟了5中场景测试调用的是拷贝构造函数还是赋值运算符。执行结果如下 分析如下 A. 这是虽然使用了但是实际上使用对象p来创建一个新的对象p1。也就是产生了新的对象所以调用的是拷贝构造函数。 B. 首先声明一个对象p2然后使用赋值运算符将p的值复制给p2显然是调用赋值运算符为一个已经存在的对象赋值 。 C. 以值传递的方式将对象p2传入函数f内调用拷贝构造函数构建一个函数f可用的实参。 D. 这条语句拷贝构造函数和赋值运算符都调用了。函数f1以值的方式返回一个Person对象在返回时会调用拷贝构造函数创建一个临时对象tmp作为返回值返回后调用赋值运算符将临时对象tmp赋值给p2. E. 按照4的解释应该是首先调用拷贝构造函数创建临时对象然后再调用拷贝构造函数使用刚才创建的临时对象创建新的对象p3也就是会调用两次拷贝构造函数。不过编译器也没有那么傻应该是直接调用拷贝构造函数使用返回值创建了对象p3。 4、深拷贝、浅拷贝 ① 通常默认生成的拷贝构造函数和赋值运算符只是简单的进行值的复制。例如上面的Person类字段只有int和string两种类型这在拷贝或者赋值时进行值复制创建的出来的对象和源对象也是没有任何关联对源对象的任何操作都不会影响到拷贝出来的对象。反之假如Person有一个对象为int *这时在拷贝时还只是进行值复制那么创建出来的Person对象的int *的值就和源对象的int *指向的是同一个位置。任何一个对象对该值的修改都会影响到另一个对象这种情况就是浅拷贝。 ② 系统提供的默认拷贝构造函数工作方式是内存拷贝也就是浅拷贝。如果对象中用到了需要手动释放的对象则会出现问题这时就要手动重载拷贝构造函数实现深拷贝。 ③ 深拷贝与浅拷贝 浅拷贝如果复制的对象中引用了一个外部内容例如分配在堆上的数据那么在复制这个对象的时候让新旧两个对象指向同一个外部内容就是浅拷贝。指针虽然复制了但所指向的空间内容并没有复制而是由两个对象共用两个对象不独立删除空间存在 深拷贝如果在复制这个对象的时候为新对象制作了外部对象的独立复制就是深拷贝。 ④ 深拷贝和浅拷贝主要是针对类中的指针和动态分配的空间来说的因为对于指针只是简单的值复制并不能分割开两个对象的关联任何一个对象对该指针的操作都会影响到另一个对象。这时候就需要提供自定义的深拷贝的拷贝构造函数消除这种影响。通常的原则是 含有指针类型的成员或者有动态分配内存的成员都应该提供自定义的拷贝构造函数在提供拷贝构造函数的同时还应该考虑实现自定义的赋值运算符 ⑤ 对于拷贝构造函数的实现要确保以下几点 对于值类型的成员进行值复制对于指针和动态分配的空间在拷贝中应重新分配分配空间对于基类要调用基类合适的拷贝方法完成基类的拷贝 5、总结 拷贝构造函数和赋值运算符的行为比较相似却产生不同的结果拷贝构造函数使用已有的对象创建一个新的对象赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符主要是否有新的对象产生。关于深拷贝和浅拷贝。当类有指针成员或有动态分配空间都应实现自定义的拷贝构造函数。提供了拷贝构造函数最后也实现赋值运算符。 对象不存在且没用别的对象来初始化就是调用了构造函数 对象不存在且用别的对象来初始化就是拷贝构造函数 对象存在用别的对象来给它赋值就是赋值函数。