2 构造、析构、赋值运算
来源:互联网 发布:淘宝有正品阿迪达斯吗 编辑:程序博客网 时间:2024/06/01 08:00
2017年8月11日 08:59:53
5 了解C++默认编写并调用哪些函数
空类,默认生成:默认的无参构造函数、copy构造函数、析构函数、赋值运算符
即:
class Empty { }; / sizeof(Empty) == 1; 空类的大小为1,因为已经声明,需要分配实际的地址,不然无法调用。
等价于: / 编译器默认生成为 1,编译器不同,大小也可能不一样
class Empty
{
Empty();
~Empty();
Empty(const Empty &);
Empty& operator=(const Empty&);
};
编译器默认生成的析构函数,为非虚,除非类本身继承了 带有虚析构函数的 父类
默认的copy和赋值,都是浅拷贝,单纯地将来源对象的每一个 non-static 成员变量拷贝到目标对象。
当声明了一个构造函数时,编译器不再生成默认的构造函数,但copy,赋值,还是会创建。
如果类中有reference成员(引用,或者const指针),并且要支持 赋值操作,必须自己定义赋值运算符。
一方面:深拷贝和浅拷贝;
另一方面:引用不能指向不同类型。更改const也不合法,所以编译器不知道在默认的赋值运算符中,如何赋值。
如果基类的 = 为private,那么编译器将拒绝为 派生类生成 = ,因为编译器默认的 = ,可以调用基类的 = 来处理基类的成员函数,
但此时基类的为私有,无法调用。
6 如果不想使用编译器默认生成的函数,应该明确拒绝
一般来说,将成员函数声明为private而且故意不实现它们。
原因: * private 可以阻止编译器默认生成(编译器默认生成的都是public)
* 如果实现了,那么friend 和 member 可以调用。
(不实现的话,链接器会发生抱怨)。
可以声明一个基类,将需要阻止的成员函数,声明为私有,自定义的函数继承这个基类。
这样自定义函数,也无法使用 对应的 私有成员函数。
比如: 基类的copy构造是私有,那么派生类,就不能用copy构造函数,不然编译器会报错。
7 为多态基类声明为 virtual析构函数
当类中存在virtual函数,说明该类试图做一个基类,此时必须有virtual析构函数,不然在发生多态时,派生类可能无法析构,造成内存泄漏。
如果类中不存在virtual,说明这个类并不想做一个基类,此时不应该有virtual析构函数。
因此:只有当class内至少有一个virtual函数,才为它声明virtual析构函数。(因为这种标准只适合用在多态性质上)
标准的string,STL,都不带虚析构函数
为抽象class声明一个 纯虚析构函数。这样既定义了抽象类,又保证了其他函数不用成为 pure virtual
8 别让异常逃离析构函数
* 析构函数不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下他们或者程序结束。
(在析构函数内 try 之后,就 catch)
* 如果客户需要对某个操作函数运行器件抛出的异常做出反应,那么class应该提供一个普通函数(而非析构函数中)执行该操作。
9 绝不要在构造和析构过程中调用virtual函数
因为在构造器件,virtual函数不是真的virtual函数,不会下降到派生类那一层,而是调用基类的virtual函数。
派生类对象的基类构造期间,对象的类型是基类,(即使用RTTI检测,也是),virtual函数会被编译器解析至base class
例如:
class A
{
public :
A()
{
fun();
}
virtual void fun();
};
class B : public A
{
public :
virtual void fun();
};
B b; 这句话,将会调用A::fun(),而不是 B 中的
同样的道理,也运用在析构函数之中。
注意:
有时候,构造函数,调用非虚函数,但是该非虚函数,会调用虚函数,此时比较隐蔽。
可以使用辅助函数创建一个值传给base class 构造函数。(向上传值)
如果此函数为static,就不可能意外指向 在派生类构造初期,没有初始化的成员变量。
10 令 operator= 返回一个reference to * this
这是一个协议,并无强制性。
A& operator=(const A& a)
{
...
return *this;
}
11 在 operator= 中处理"自我赋值"
例如:
class Bitmap {...};
class Widget
{
...
private :
Bitmap *pb;
};
方案一:
Widget& operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
该方案错误,当自我赋值时,会把rhs.pb先删掉
方案二:
Widget& operator=(const Widget& rhs)
{
if(this == &rhs)
return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
当new失败了,pb会指向一个被删除的空间,异常不安全
方案三:
Widget& operator=(const Widget& rhs)
{
Bitmap *temp = pb;
pb = new Bitmap(*rhs.pb);
delete temp;
return *this;
}
new 抛出异常,那么pb还会指向原来的空间,并没delete,保持不变
一种代替的方法:
使用 copy and swap 技术,来保证赋值的同时,又保证异常安全
Widget& operator=(const Widget& rhs)
{
Widget temple(rhs);
swap(temple); //自定义的swap
return *this;
}
12 复制对象时勿忘其每一个成份
在发生组合时:
class A { .. };
class B
{
public :
B(const B & b)
{
.... 此时不要忘记初始化a,不然会调用a的默认构造函数
}
private:
A a;
};
在发生继承时:
class A { .. };
class B : public A
{
public :
B(const B & b) : A(b) 不要忘记调用基类的copy构造函数,不然会调用基类的默认构造函数
{
....
}
B& operator=(const B& b)
{
...
A::operator=(b); 调用基类的赋值操作,来赋值基类的成员变量
...
}
};
如果copy构造函数 和 赋值运算符,有很多重复的动作。
注意:不可以相互调用,来节省代码。
而应该将这些重复动作,定义成一个共用的函数,copy构造 和 赋值运算符,都来调用。
原因:
* 赋值运算符 调用 copy构造,那么会试图建立一个已经存在的对象
* copy构造 调用 赋值运算符,毫无意义,因为对象还没构造完成,哪来的赋值运算符呢?
阅读全文
0 0
- 2构造/析构/赋值运算
- 2 构造、析构、赋值运算
- 构造/析构/ 赋值运算
- 构造,析构,赋值运算
- Effective C++ --2 构造/析构/赋值运算
- 二.构造/析构/赋值运算
- 2.构造/析构/赋值运算
- [C++] 构造/析构/赋值运算
- 构造/析构/赋值运算符
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之一)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之二)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之三)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之四)
- Effective C++读书笔记2(构造/析构/赋值运算)
- Effective C++之2 构造/析构/赋值运算
- ec++ 的笔记(2) ----- 构造/析构/赋值运算
- C++(2)构造/析构/赋值运算
- Effective C++读书笔记---构造/析构/赋值运算
- python中文匹配
- C/C++内存管理详解 堆 栈
- 关于《ERP原理》的读书笔记和思考(一)_ERP演变溯源
- List根据里面的对象属性的值进行排序
- Fragment嵌套Fragment时候。子类fragment调用父容器Fragment方法
- 2 构造、析构、赋值运算
- STL内存分配
- mysql5.6及以上版本安装后修改root密码
- Xcode error: unable to dequeue a cell with identifier cell
- Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇
- jQuery的data函数使用遇到的坑
- html一个音乐播放界面
- R语言--中文资料下载
- GPU编程自学8 —— 纹理内存