c++OOP之复制控制 ------复制构造函数、赋值重载、析构
2014-09-26 02:01
204 查看
本博文我们讨论OOP复制控制的一些内容;
首先考虑对象复制的时机: 非引用类型
1):根据一个类去显式或者隐式初始化一个对象;
2):复制一个对象,将它作为实参传给一个函数;
3):从函数返回时复制一个对象。( string tolittle(string word))
4):初始化顺序容器中的元素。(例如vector 必须具备 copy,assignment功能)
一:复制构造函数:
复制构造函数调用的时机就是在对象复制的时候。方式有两种:
1):当用户不提供时,编译器自动为我们合成一个拷贝构造函数;
示例代码如下:
2):用户自己定义:
放入class Student 的public中即可;
复制构造函数之 深copy 和 浅copy;
示例代码:
二: 赋值运算符的重载:【因为我们自定义的类没有内置比较运算符(<、>、!=)】
示例代码如下:
禁止类复制和赋值的方法:
a):把 copy构造函数和复制运算符设为private;
b):只声明,不实现;
首先考虑对象复制的时机: 非引用类型
1):根据一个类去显式或者隐式初始化一个对象;
2):复制一个对象,将它作为实参传给一个函数;
3):从函数返回时复制一个对象。( string tolittle(string word))
4):初始化顺序容器中的元素。(例如vector 必须具备 copy,assignment功能)
一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。(面试)!!
11.复制构造函数、赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个。以String为例: a) 涉及到深拷贝、浅拷贝问题,所以需要提供拷贝构造函数 b) 然后,为了保持一致,赋值运算符也应该实现深拷贝 c) 既然实现深拷贝,那么必定申请了资源(例如内存),所以必然需要析构函数来手工释放。
一:复制构造函数:
复制构造函数调用的时机就是在对象复制的时候。方式有两种:
1):当用户不提供时,编译器自动为我们合成一个拷贝构造函数;
示例代码如下:
#include <iostream> #include <string> using namespace std; class Student { public: Student() {} Student(int id, const string&name, int age) :id_(id),name_(name),age_(age) {} void print() { cout << id_ <<":" << name_ <<":" << age_ << endl; } private: int id_; string name_; int age_; }; int main(int argc, const char *argv[]) { Student s(11,"zhangsan",23); s.print(); Student s2(s); //执行此句,调用默认复制构造函数 s2.print(); return 0; }
2):用户自己定义:
放入class Student 的public中即可;
Student(const Student &s) { id_ = s.id_; name_ = s.name_; age_ = s.age_; }
复制构造函数之 深copy 和 浅copy;
含有指针成员变量的类在复制时,有两种选择: a) 复制指针的值,这样复制完毕后,两个对象指向同一块资源,这叫做 浅拷贝shallow copy(有可能出错) b) 复制指针所指向的资源,复制完毕后,两个对象各自拥有自己的资源,这叫做 深拷贝 deep copy
示例代码:
#include "iostream" #include "string.h" using namespace std; //copy String 时,仅仅复制指针的值 //导致析构时发生了问题 class String { public: String(); String(const char*s); String(const String &s); ~String(); void print()const { cout << str_ << endl; } private: char *str_; }; String::String() :str_(new char(0)) { } String::String(const char *s) :str_(new char[strlen(s)] +1) { ::strcpy(str_, s); } //程序结束时会调用析构函数,两个对象s1,s2必然会析构两次,而str_指向同一片区域; //当析构s1时,s1.str_所指向的区域被释放;而s2.str_ 也同时被释放 //当析构s2时,由于s2.str_已经被s1.str_释放,所以会产生错误. String::String(const String &s) :str_(s.str_) {} /* 修改成以下即可: *String::String(const String &s) * :str_(new char[strlen(s)] +1) * { * ::strcpy(str_, s.str_); * } */ String::~String() { delete[] str_; //注意 } int main(int argc, const char *argv[]) { String s("helloworld"); s.print(); String s2(s); s.print(); return 0; }
二: 赋值运算符的重载:【因为我们自定义的类没有内置比较运算符(<、>、!=)】
示例代码如下:
#include <iostream> #include <string.h> using namespace std; class String { public: String(); String(const char *s); String(const String &s); ~String(); String &operator = (const String &s); size_t size()const //自己构造size函数 { return strlen(str_); } void print() { cout << str_ << endl; } private: char *str_; }; String::String() :str_(new char[1]) { *str_ = 0; } String::String(const char*s) :str_(new char(strlen(s)+1)) { ::strcpy(str_, s); } String::String(const String &s) :str_(new char(strlen(s.str_)+1)) { ::strcpy(str_, s.str_); } String &String::operator = (const String &s) { if(this == &s)//自身赋值 return *this; delete[]str_; str_ = new char(s.size() + 1 ); ::strcpy(str_, s.str_); return *this; } String::~String() { delete[] str_; } int main(int argc, const char *argv[]) { String s("helle"); s.print(); String s2; s2 = s; //赋值重载 s2.print(); s2 = s2 ; //注意自身赋值情形 s2.print(); return 0; }
禁止类复制和赋值的方法:
a):把 copy构造函数和复制运算符设为private;
b):只声明,不实现;
#include <iostream> #include <string> #include <vector> using namespace std; //no copy,no assignment //google 推荐写法 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&) class Test { public: Test(){} ~Test(){} private: Test(const Test&t); void operator=(const Test &t); }; int main(int argc, const char *argv[]) { Test t; Test t2(t);//error Test t3; t3 = t; //error return 0; }
a):如果一个类,不需要复制和赋值,那就禁用这种能力,这可以帮助避免大量潜在的bug。 b):如果一个类,实现了像value一样的复制和赋值能力(意味着复制和赋值后, 两个对象没有任何关联,或者逻辑上看起来无任何关联),那么就称这个类的对象为 值语义(value semantics)。 如果类不能复制,或者复制后对象之间的资源归属纠缠不清,那么称为 对象语义(object semantics),或者 引用语义(reference semantics)。
相关文章推荐
- 批注:C++中复制构造函数与重载赋值操作符总结:默认浅拷贝,带指针的需要深拷贝
- C++语法基础--复制控制--复制构造函数 ,赋值操作符 ,析构函数
- C++ 第13章 复制控制---复制构造函数、赋值操作符、析构函数
- C++之重载String ----- 构造函数、复制控制、重载操作符
- C++中复制构造函数与重载赋值操作符
- C++构造函数、复制构造函数、赋值操作符、析构函数--个人总结
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- 聊聊C++中复制构造函数和赋值操作符
- C++构造,析构,友元类,对象,static成员,复制构造函数,运算符重载杂谈
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- 复制控制:复制构造函数、赋值操作符、析构函数
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容 【转】 参考度4.6星
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- 【c++primer——】第13章复制控制——01深入理解复制构造函数
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++ 类型自动转换 构造函数 复制构造函数 赋值操作运算符函数
- c++ 初学 构造函数和复制控制