Effective C++ 45-48

45。弄清c++在幕后为你所写,所调用的函数。

如果设置一个空类,c++编译器会声明以下函数:拷贝构造函数,赋值运算符,析构函数,一对析构函数(const和非const)。而如果你没有声明任何构造函数的话,编译器会为你声明一个缺省构造函数。这些函数都是公有的。

编译器生成的缺省构造函数和析构函数实际上什么也不做,生成的析构函数一般是非虚构的,除非继承了一个具有虚析构函数的基类。缺省取地址符只是返回对象的地址,即return this。而拷贝构造函数和赋值运算符,对类的非静态数据成员进行“以成员为单位”逐一拷贝构造或赋值,也就是浅拷贝。

当类中有引用时,默认的拷贝函数无法实现,编译器会报错,有常量也是,有指针是,会发生浅拷贝但是运行上没有错误。对于含有指针,引用和const成员的类需要自己定义赋值运算符和复制构造函数。而如果将派生类中的赋值运算符或拷贝构造函数声明为private,编译器也会拒绝为这个派生类生成相应的赋值运算符和拷贝构造函数。

46.宁可编译和链接时出错,也不要在运行时出错。

当通过编译和链接后,只有极少数情况会让C++抛出异常,如内存耗尽,运行时错误和C++没什么关系。C++没有运行时检测,要尽量避免运行时错误。

对于运行时错误,在一个运行中没有错误,并不表示其就是正确的了,因为每次程序运行的状态都不一样。

而避免运行时错误的一般方法是对设计做一些小小的改动,就可以在编译期间消除可能产生的运行时错误。一般设计在程序中增加新的数据类型,以在编译时检测数据的安全性。

对于一个日期类,有构造函数:Date(int day,int month,int year);实现这个类面临的问题是对day和month进行合法性检测,如果不进行检测,由于其内部逻辑可能会导致一些运行时错误。一种简单的方法是使用枚举

enum Month {Jan = 1,Feb = 2,....,Dec =12};

而构造函数改为:

Date(int day,Month month,int year);

但是这样做没有多大好处,因为枚举类型不用初始化,即直接 Date d(1,Month m,2014),能通过编译,但是运行时出错。

即想免除运行时检查,又要保证足够的安全性,选择使用一个类来实现month。

class Month{
public:
	static const Month Jan(){return 1;}//这里其实是调用隐式构造函数,其实返回值为 Month(1);
	//....
	static const Month Dec(){return 12;}//使用静态函数,返回一个常量,防止随意改动
	int toInt() const
	{return number;}
private:
	Month (int n):number(n){}
	const int number;
};

这里调用类的静态成员返回对应的Month,而构造函数隐藏,防止用户自己去创建新的month。但即使有了这样的类,用户还是可以指定一个非法的month,如下:

	Month* m;
	Data(1, *m ,2014);

消除所有的运行时检测是不切实际的。但将检查由运行时转移到编译或链接时一直值得努力的目标,这样做,会使程序更小,更快,更可靠。

47.确保非局部静态对象在使用前被初始化。

使用对象前一定要初始化。

非局部静态对象是指 : 定义在全局或名字命名空间内,或在一个类中被声明为static,或在一个文件范围内被定义为static。就是值全部的对象,去掉非静态 的局部变量 和函数内的静态变量。

当类依赖与这些非局部静态对象时,如在 一个文件中有一个全局对象theCountry, 在另外一个文件中有一个对象theCity,对city的初始化依赖与country的初始化。而程序的正确运行依赖于它们的初始化顺序。但确定非局部静态对象初始化的正确顺序很困难,在多个编译单元中确保每个这样的对象初始化是很困难的,尤其当程序变得更加复杂增加更多的这种非局部静态对象的情况下。

单一模式,将每个非局部静态对象转移到函数中,声明其为static,其次,让函数返回这个对象的引用。这样用户就可以通过函数调用来指明对象,即用函数内部的static对象来取代非局部静态对象。因为对于函数的静态对象什么时候被初始化,c++明确的指出了。这样的另一个好处是如果这个模拟非局部静态对象从没被调用,也就永远没有对象构造和销毁的开销。简单的例子:

class country{....};
country& theCountry(){
	static country tc;//定义和初始化theCountry
	return tc;//返回它的引用。
}

48.重视编译器警告。

一般程序员都会忽略编译器警告,毕竟没有出错。要理解编译器的各种警告的含义。书上举个例子:

class A{
public:
	virtual void f() const{cout<<"fA";}
};
class B:public A{
public:
	virtual void f(){cout<<"fB";}
};

书上说有编译器在这里会出一个 B::f() hides virtual A:::f()的错误,即A中声明的f函数并没有在B中重新定义,但是被B中新声明的非const的f函数给隐藏了。但是这样没有实现多态,对于使用声明为A的指针的B的对象指向f函数的话,会调用A中的f函数。但是我用的vs2012中并没有提示这条警告。

时间: 2024-10-07 19:55:40

Effective C++ 45-48的相关文章

Effective C++ Item 48 认识 template 元编程

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:Template metaprogramming (TMP, 模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率 示例1: template<typename IterT, typename DistT> void advance(IterT &iter, DistT d){ if(typeid(typename std::iterator_t

读书笔记 effective c++ Item 48 了解模板元编程

1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在C++编译器内部运行的一个程序,它的输出——从模板中实例化出来的C++源码片段——会像往常一样被编译. 2. 使用TMP的优势 如果这没有冲击到你,是因为你没有足够尽力去想. C++不是为了模板元编程而设计的,但是自从TMP早在1990年被发现之后,它就被证明是非常有用的,为了使TMP的使用更加容易

Effective C++ 条款48 认识template元编程

1. 模板元编程(template mataprogramming,TMP)是编写C++程序并执行于编译期的过程,"所谓template mataprogram(模板元程序),是以C++写成,执行于C++编译器内的程序.一旦TMP程序结束执行,其输出,也就是从templates具现出来的若干C++源码,便会一如往常地被编译". 2. 自从templates加入C++,TMP底层特性便被引进了,自从TMP于1990s初期被发现,TMP被日渐证明十分有用.TMP有两个伟大效力: 1). 它

Effective C++ 条款48

本节条款:了解模板元编程 本节条款是对模板元编程的简单介绍,让读者知道有这么一种编程方式,更确切的说是一种技术. 那么,什么是模板元编程?模板元编程有什么好处?按照作者的原话就是: 1. TMP可将工作由运行期转移到编译期,因而得以实现早期错误侦测或者更高的执行效率. 2. TMP可被用来生成"基于政策选择组合"的客户定制代码,也可以用来避免生成对某些特殊类型并不适合的代码. 说原理容易让人不知所云,来一段作者的代码,大家看看. template <int N> struc

python3之模块psutil系统性能信息

psutil是个跨平台库,能够轻松实现获取系统运行的进程和系统利用率,包括CPU.内存.磁盘.网络等信息. 它主要应用于信息监控,分析和限制系统资源及进程的管理.它实现了同等命令命令行工具提供的功能,如:ps.top.lsof.netstat.ifconfig.who.df.kill.free.nice.ionice.iostat.iotop.uptime.pidof.tty.taskset.pmap等.目前支持32位和64位的linux.windows.OS X.FreeBSD和Sun Sol

邮件服务

邮件语言:M4 SASL: v2 cyrus-sasl 认证框架 courier-authlib MTA: 邮件传输代理,SMTP服务器 sendmail,  UUCP qmail 数学家当程序员,他写的算法绝对是一流的 postfix 安全.兼容.效率高 exim Exchange (异步消息协作平台) 重量级 MDA: 邮件投递代理 procmail maildrop MRA: 邮件检索代理(pop3, imap4) cyrus-imap dovecot MUA: 邮件用户代理 Outloo

多线程修改同一个数据

个人理解: GIL:存在于Cpython中,称为全局解释器锁,在同一时间只能一个python线程在跑,但这并不是说是串行运行的,他还是“并行”的,CPU在不断的分配cpu时间给每个线程去运行,只是同一时间刻只有一个线程在跑. 线程锁:只让一个线程运行加锁的那段代码. 示例: 1 print(n) 2 n += 1 3 print(n) 4 # 这里三条语句需要三次获取n的值,第一次print(n)和 n += 1时拿到的n 不一定相同,第二次print(n)也不一定是 n += 1的结果. 但

【BZOJ-1974】auction代码拍卖会 DP + 排列组合

1974: [Sdoi2010]auction 代码拍卖会 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 305  Solved: 122[Submit][Status][Discuss] Description 随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代码库.猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库.iPig不想把代码库给所有想要的小猪,只想给其中的一部分既关系好又肯出钱的小猪,于是他决定举行了一个超大型拍

RAC集群节点故障模拟测试

RAC节点故障模拟测试 重启单个RAC 节点模拟测试模拟操作步骤使用shutdown –Fr的方式重启节点,查看系统反应和数据库重新启动的时间.预期测试结果重启单个节点,vip将会切换到另外一个节点.系统重新启动之后,节点上的集群服务和数据库将会自动启动,重新加入集群.Vip也将切换回原始节点.测试过程记录使用shutdown 命令重启第三节点第三节点关闭之后查看crs服务状态RAC02:oracle:db2 > crs_stat -tName           Type           

【Sprint3冲刺之前】日历表的事件处理和管理(刘铸辉)

我的Sprint2冲刺--日历表的事件处理和管理(刘铸辉,刘静) 我的Sprint2冲刺计划领到的任务是和静姐结对编程,完成日历表的事件处理和管理,下面详细讲解下技术细节. 1.设计结构图 首先要画出整个UI设计图,方便理解这个日历布局和日历样式的绘制. 这里总共分解为两个 View 文件: 1:calender_main.xml(Calender.java用来绘制日历表和日历表中的事件) 作为启动的主画面,新建项目时,首先生成. schedule_toptext:用来显示年月日,闰几月,年份和