Effective C++ 11-17

11.为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符。

显然,由于动态内存分配,绝对会有深浅拷贝的问题,要重写拷贝构造函数,使其为深拷贝,才能实现真正意义上的拷贝。这是我理解的关于要声明拷贝构造函数的原因。

而对于赋值操作符,类似的道理。

	A b = a;
	b = a;

对于上述两种形式,上面调用的是复制构造函数,而下面才是 赋值操作符=。赋值与复制很相似,缺省的操作都是将类的全部成员进行复制。

深拷贝主要的操作很简单,对于指针,动态申请一块内存来存放指针指向的数据,每个指针都指向自己的一块内存,而不是其他人的。

12.尽量使用初始化而不要在构造函数里赋值。

即尽量使用成员初始化列表,而不是使用赋值的方法。

首先对于const成员和引用,只能使用初始化列表来初始化。其次初始化列表效率更高,因为对象的创建分为两步:数据成员的初始化和执行被调用构造函数体内的动作。即使用赋值之前,先进行了数据成员的初始化,然后才是赋值。所以使用初始化列表效率更高。

但当有大量的固定类型的数据成员要在每个构造函数中以相同的方式初始化的时候,使用赋值会更加合理一点。

13。初始化列表中成员列出的顺序和它们在类中声明的顺序相同。

这是因为初始化列表的顺序并不影响初始化的顺序,初始化的顺序是有成员在类中声明的顺序决定的,而让其顺序相同是使程序看起来是按照初始化列表的顺序初始化。

而c++不使用初始化列表的顺序的原因是:对象的析构函数是按照 与成员在构造函数中创建的相反的顺序 创建的。则如果对象不是按照一种固定的顺序来初始化,编译器就要记录下每一个对象成员的初始化顺序,这将带来较大的开销。

14、确定基类有虚析构函数。

通过基类的指针去删除派生类的对象时,基类一定要有虚析构函数,不然 会有不可预测的后果。不使用虚析构函数,只调用基类的析构函数去删除派生类对象,这是无法做到,也是无法确定后果的。

构造函数调用是先基类后派生类,而析构函数的顺序是先派生类后基类。

当一个类不作为基类使用时,使用虚析构函数是一个坏主意。因为虚函数的对象会有一个虚指针指向虚表,会浪费空间来储存这个没有意义的指针。

纯虚函数在虚函数后加 =0 即可。

15.让operator = 返回 *this 的引用。

= 号可以连接起来,因为其返回值的原因,声明operator=的形式如下:

C& C:: operator= (const C&);

其输入和返回都是类对象的引用。以实现连续的赋值操作。返回值是 = 左边值的引用 即 *this的引用,因为右边即参数是const类型的。

函数的参数是 const类型的原因,是 = 右边的值经过计算会获得一个新的结果,而这个对象要用一个临时对象来储存,这个临时对象是const类型的,因为其作为函数的参数,且不能被函数修改。

16.在operator = 中对所有数据成员赋值。

对于深拷贝要自己写一个更加正确的 = 操作。

在涉及继承时,派生类的赋值运算必须处理基类的赋值。如果重写派生类的赋值运算,就必须同时显示的对基类部分进行赋值。

class A{
public:
	int a;
	A(int x):a(x){}
//	A& operator=(const A& x){ a = x.a; return *this;}
};
class B:public A{
public:
	int b;
	B(int x):A(x),b(x){}
	B& operator=(const B&);
};
B& B::operator=(const B& x){
	if(this == &x)return *this;
//	A::operator=(x);//调用基类的赋值函数要如此写
	static_cast<A&>(*this) = x;//也可以强制转换为A类型然后在调用基类的默认赋值函数
	b = x.b;
	return *this;
}

拷贝构造函数也是如此,也要对基类的成员进行复制,只要在成员初始化列表中添加基类即可。

17.在operator=中检查给自己赋值的情况。

这是基于效率考虑的,在赋值的首部检测是给自己赋值,就立即返回,如16中函数所写的那样。这里除了类中自己成员的赋值,如果有基类,还要调用基类的赋值函数,会增加开销。

另一个原因是保证正确性。一个赋值运算符必须首先释放掉一个对象的资源,如有些指针指向了动态申请的空间,则赋值前一般要释放这些资源,然后在指向新的资源(如果在赋值开始,用些临时的指针来记录之前的所有指针指向的内存,然后在赋值后再将临时指针指向的内存全部释放。这还是不行,因为对这些指针如p,必须有 p = new p[]...来指向新申请的一块空间,而如果是同一个对象的话,这里就将原来的指针指向一个新的地址,且两者相同了,所以又需要一个新的临时指针来指向赋值对象的指针的值),也就是说为了使其给自己赋值,对与每个指针必须新建两个指针,一个储存左边的对象的原指针,一个储存右边的对象的原指针,这样的开销时极大极浪费的,也是没有必要的,为了一些不应该进行的为自己赋值要提前准备大量内存来储存数据,这也是不科学的。

所以最好的解决办法是检测是否为自己赋值,一般采用检测对象地址是否相等,c++中一般采取这种方法。对于java中,不好的做法是检测对象是否相等,即其全部值是否完全相等,较好的方法是根据对象的id来判断对象是否为同一个对象。

时间: 2024-10-12 17:22:49

Effective C++ 11-17的相关文章

2015.11.17 新起点,出发。

今天注册了博客,要开始认真仔细的学习前端技术了.这个博客将作为我学习进度记录.学习笔记整理.心得体会整理等书写记录的工具. —希望苍天不负有心人 2015.11.17

Effective C++ 条款17 以独立语句将newed对象置入智能指针

  对于函数: int priority(); void processWidget(std::tr1::  shared_ptr<Widget> pw,int priority); 调用以上函数 processWidget(new Widget,priority()); 以上调用错误,因为shared_ptr构造函数需要一个原始指针,但该构造函数是个explicit构造函数,无法进行隐式转换. 而且其调用顺序也无法确定. 所以,我们一般使用分离语句,创建Widget,然后置入只能指针中.最后

11.14/11.15 Apache和PHP结合 11.16/11.17 Apache默认虚拟主机

11.14/11.15 Apache和PHP结合 编辑:httpd主配置文件/usr/local/apache2.4/conf/httpd.conf 去掉#号 将php7加# Telnet IP+80 端口不通,需要开启iptables防火墙 更改require 更改配置后需要操作才能生效 重新加载服务 增加一行php进行解析 页面显示 结果访问的是源代码 如果遇到php无法解析,需要检查相关的apache的配置文件 (1)检查apache的php 有没有加载php5 查看是否有libphp5.

2015.11.17

CommonJS CommonJs是服务器端模块的规范,Node.js采用了这个规范. 根据CommonJS规范,一个单独的文件就是一个模块.加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象. 例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // foobar.js //私有变量 var test = 123; //公有方法 function foobar () {     this.foo =

17.11.17 递归作业

习题(15-1) 前缀表达式 (1010) 描述 前缀表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的前缀表示法为+ 2 3.前缀表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的前缀表示法为* + 2 3 4.本题求解前缀表达式的值,其中运算符包括+ - * /四个. 关于输入 输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数. 关于输出 输出为一行,表达式的值.  可直接用printf("%f\n", v

Effective Item 11 - 复合模式优于继承

继承是实现代码重用的方法之一,但使用不当则会导致诸多问题. 继承会破坏封装性,对一个具体类进行跨包访问级别的继承很危险. 即,子类依赖父类的实现细节. 如果父类的实现细节发生变化,子类则可能遭到破坏. 举个例子,扩展HashSet,记录HashSet实例创建以来一共进行了多少次添加元素的操作. HashSet有两个添加元素的方法--add(E e)和addAll(Collection<? extends E> c). 那就覆盖这两个方法,在添加操作执行前记录次数: public class I

2016/11/17

1.单例设计模式 (1)懒汉模式 1 public class Singleton { 2 3 private Singleton() {} 4 private static Singleton singleton = null; 5 // 这里可以加同步锁解决线程安全问题 6 public static Singleton getInstance() { 7 if(singleton == null) { 8 singleton = new Singleton(); 9 } 10 return

More Effective C++ 条款17 考虑使用lazy evaluation(缓式评估)

1. lazy evaluationg实际上是"拖延战术":延缓运算直到运算结果被需要为止.如果运算结果一直不被需要,运算也就不被执行,从而提高了效率.所谓的运算结果不被执行,有时指只有部分运算结果被需要,那么采用拖延战术,便可避免另一部分不被需要的运算,从而提高效率,以下是lazy evaluation的四种用途. 2. Reference Counting(引用计数) 如果要自己实现一个string类,那么对于以下代码: String s1="Hello"; S

Scrum Meeting 11 -2014.11.17

今天和其他两个小组讨论了关于整合问题,在数据库连接等具体方面上还需要继续商讨. 我们小组内部讨论了,这周还是需要在处理整合的同时做项目整体的测试与改进的. Member Today’s task Next task 林豪森 与其他小组商讨整合问题 与其他小组商讨整合问题 宋天舒 优化代码结构,添加注释 测试项目功能实现 张迎春 修复整合存在的bug 测试项目功能实现 黄漠源 优化代码结构,添加注释 优化代码结构,添加注释 黄敬博 修复整合存在的bug 优化代码结构,添加注释 刘翔宇 优化pdf的

Notes of Daily Scrum Meeting(11.17)

今天是第四周的周一,也就是说距离最后发布也只剩下一周的时间,但我们的工程里面还有很多的问题没有解决,我关注过 其他一两个小组,他们的程序基本功能已经都实现了,确实心里蛮紧张的,希望大家给力!昨天是周日,大家都想休息了,所以 就没有继续.我昨天依照现在工作的进度加上了一些工作任务,燃尽图中会有体现,下面的表格是我加上的工作: 队员 工作内容 时间 高孟烨 UI界面的完善和调试 6h 邓亚梅 UI界面的完善和调试 4h 雷元勇 程序测试 4h 王迪 程序测试 4h 下面是今日的团队任务总结: 团队成