作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.youkuaiyun.com/gzshun
前面学习了构造函数的用法与初始化列表的特性,本章还是学习构造函数的内容。类型还能控制复制、赋值或撤销该类型的对象,这时候,类通过特殊的成员函数:复制构造函数、赋值操作符和析构函数来控制这些行为。这些都属于类对象的复制控制,挺重要的一部分内容。
string str1("hello");
string str2(str1);
2.赋值操作符的例子:
string str1("hello");
string str2 = str1;
二、复制构造函数的应用于哪些地方
什么样的函数才是复制构造函数?
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰)。
复制构造函数用于:
1.用一个同类型的对象显示或隐式初始化一个对象。
eg:
string str1("hello");
string str2(str1);
2.作为函数的实参
eg:
void Show(string str);
...
string str1("hello");
Show(str1); //存在一个复制操作,将str1对象隐式复制到Show函数的临时对象
string getString() const
{
string str("hello");
...
return str;
}
...
string str1 = getString(); //将getString函数的返回值的string对象复制到str1对象
vector<string> vec(5); //声明了一个vector,包含5个string对象;
//编译器首先使用string默认构造函数创建一个临时对象来初始化vec,然后使用复制构造函数将临时值复制到vec的每个元素中。
a.每个元素调用一次默认构造函数
string str[10]; //调用了10次默认构造函数
b.每个元素调用一次复制构造函数
string str1, str2, str3, str4;
string str[] = { str1, str2, str3, str4};
三、合成的复制构造函数和合成的赋值操作符
如果用户没有显示的定义复制构造函数或赋值操作符,编译器将会默认的合成一个复制构造函数。复制构造函数与合成复制构造函数,或者赋值操作符与合成赋值操作符不同,编译器默认合成的函数的行为是"逐个成员初始化",将新对象初始化为原对象的副本。逐个成员初始化不包括static成员,只负责初始化非static成员。
四、小试牛刀
执行结果:
四、禁止复制
1.有些类需要完全禁止复制,怎么办?
只需将复制构造函数声明为私有成员函数即可。
解释:因为如果不声明定义,编译器将会默认合成一个复制构造函数。
例子:
编译报错:
编译报错:
undefined reference to `CObj::CObj(CObj const&)'
这也正是这里需要的结果,禁止复制成功,OK。
来源:http://blog.youkuaiyun.com/gzshun
前面学习了构造函数的用法与初始化列表的特性,本章还是学习构造函数的内容。类型还能控制复制、赋值或撤销该类型的对象,这时候,类通过特殊的成员函数:复制构造函数、赋值操作符和析构函数来控制这些行为。这些都属于类对象的复制控制,挺重要的一部分内容。
一、本文内容的应用
1.复制构造函数的例子:string str1("hello");
string str2(str1);
2.赋值操作符的例子:
string str1("hello");
string str2 = str1;
二、复制构造函数的应用于哪些地方
什么样的函数才是复制构造函数?
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰)。
复制构造函数用于:
1.用一个同类型的对象显示或隐式初始化一个对象。
eg:
string str1("hello");
string str2(str1);
2.作为函数的实参
eg:
void Show(string str);
...
string str1("hello");
Show(str1); //存在一个复制操作,将str1对象隐式复制到Show函数的临时对象
3.作为函数的返回值
eg:string getString() const
{
string str("hello");
...
return str;
}
...
string str1 = getString(); //将getString函数的返回值的string对象复制到str1对象
4.初始化顺序容器的元素
eg:vector<string> vec(5); //声明了一个vector,包含5个string对象;
//编译器首先使用string默认构造函数创建一个临时对象来初始化vec,然后使用复制构造函数将临时值复制到vec的每个元素中。
5.根据元素初始化式列表初始化数组元素
这里要强调的是:初始化式列表a.每个元素调用一次默认构造函数
string str[10]; //调用了10次默认构造函数
b.每个元素调用一次复制构造函数
string str1, str2, str3, str4;
string str[] = { str1, str2, str3, str4};
三、合成的复制构造函数和合成的赋值操作符
如果用户没有显示的定义复制构造函数或赋值操作符,编译器将会默认的合成一个复制构造函数。复制构造函数与合成复制构造函数,或者赋值操作符与合成赋值操作符不同,编译器默认合成的函数的行为是"逐个成员初始化",将新对象初始化为原对象的副本。逐个成员初始化不包括static成员,只负责初始化非static成员。
四、小试牛刀
这里编写一个最简单的例子,来展示一下类对象的复制控制。
#include <iostream> using namespace std; class CObj { public: CObj() { cout << "默认构造函数" << endl; } CObj(const CObj &obj) { cout << "复制构造函数" << endl; } CObj & operator=(const CObj &obj) { if (&obj != this) { cout << "赋值操作符" << endl; } return *this; } }; int main() { CObj obj1; cout << endl; CObj obj2(obj1); cout << endl; CObj obj3; obj3 = obj2; return 0; }
执行结果:
默认构造函数 复制构造函数 默认构造函数 赋值操作符
四、禁止复制
1.有些类需要完全禁止复制,怎么办?
只需将复制构造函数声明为私有成员函数即可。
解释:因为如果不声明定义,编译器将会默认合成一个复制构造函数。
例子:
#include <iostream> using namespace std; class CObj { public: CObj() { cout << "默认构造函数" << endl; } private: CObj(const CObj &obj) { cout << "复制构造函数" << endl; } }; int main() { CObj obj1; CObj obj2(obj1); return 0; }
编译报错:
error: 'CObj::CObj(const CObj&)' is private
这就是禁止复制的效果,正是这里想要的。
2.连友元函数和成员函数的复制也要禁止,怎么办?
如果想要连友元函数和成员函数的复制也禁止,那么可以声明一个私有的复制构造函数但不对其定义。
解释:因为友元函数或成员函数可以访问到类的私有成员,所以当然能调用私有的复制构造函数,所有将复制构造函数声明为私有但不定义,就能避免友元或成员函数的调用。
例子:
#include <iostream> using namespace std; class CObj { public: CObj() { cout << "默认构造函数" << endl; } friend void fcopy(); private: CObj(const CObj &obj); }; void fcopy() { CObj obj1; CObj obj2(obj1); } int main() { fcopy(); return 0; }
编译报错:
undefined reference to `CObj::CObj(CObj const&)'
这也正是这里需要的结果,禁止复制成功,OK。
再接再厉!