【more effective c++读书笔记】【第1章】基础议题(1)

条款1:仔细区别pointers和references

1、pointers和references之间的区别是:

a、没有所谓的null reference,一个reference必须总代表某个对象。pointer可以不指向任何对象。

b、pointer可以被重新赋值,指向另一个对象,reference却总是指向它最初获得的那个对象。

例子:

#include<string>
#include<iostream>
using namespace std;

int main(){
	string s1("Nancy");
	string s2("Jack");

	string& rs = s1;//rs代表s1
	string* ps = &s1;//ps指向s1
	rs = s2; //rs仍代表s1,但s1的值变成了"Jack"
	cout << s1 << endl;

	ps = &s2; //ps指向s2
	cout << s1 << endl;//s1的值没有变化

	system("pause");
	return 0;
}

2、一个reference必须总代表某个对象,因此reference必须有初值,但pointer没有这样的限制。

例子:

string& rs;//错误,reference必须初始化
string s("xy");
string& rs = s;//没问题,rs指向s

string* ps;//有效,但风险高

3、“没有所谓的null reference”这个事实意味着使用references可能会比使用pointers更富效率。因为使用reference之前不用测试其有效性。

例子:

void printDouble(const double& rd){
	cout << rd; //不需要测试rd,它一定代表某个double
}
void printDouble(const double* pd){
	if (pd) //检查是否为null pointer
		cout << *pd;
}

4、考虑意向情况:

char* pc = 0;//将pointer设定为null
char& rc = *pc;//让reference代表null pointer的解引值

上述结果不可预料,C++对此没有定义,不应该写出这样的代码。

5、一般而言,当需要考虑“不指向任何对象”的可能性时,或是考虑“在不同时间指向不同对象”的能力时,应该使用pointer。当确定“总是会代表某个对象”,而且“一旦代表了该对象就不能够再改变”,或者重载某个操作符时,应该使用reference。

6、本条款结论:当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由pointers达成,你就应该选择references。任何其他时候,请采用pointers。


条款2:最好使用C++转型操作符

1、旧式转型存在以下问题:

a、它几乎允许你将任何类型转换为任何其他类型,这十分拙劣,如果每次转型都能够精确地指明意图则更好。

b、它们难以辨识,因为旧式转型的语法结构由一对小括号加上一个对象名称组成,而小括号和对象名称在C++的任何地方都有可能被使用。、

2、C++提供四种新式转型,见我另一篇博客http://blog.csdn.net/ruan875417/article/details/47281885条款27。

3、static_cast 基本上拥有与C旧式转型相同的威力与意义,以及相同的限制。static_cast 不能移除表达式的常量性(constness)。

4、const_cast 用来改变表达式的常量性(constness)或变易性(volatileness)。

5、dynamic_cast要用来执行继承体系中“安全向下转型或跨系转型动作”,也就是说可以利用dynamic_cast将“指向base class objects的pointers或references”转型为“指向derived class objects的pointers或references,并且得知转型是否成功。如果失败,将返回空指针(当转型对象是指针)或抛出异常(当转型对象是引用)。dynamic_cast只能用来协助你巡航继承层体系之中,它无法应用在缺乏虚函数的类型上,也不能改变类型的常量性(constness)。

const_cast和dynamic_cast例子:

#include<iostream>
using namespace std;

class Base{
public:
	virtual void print(){
		cout << "Base" << endl;
	}
};
class Derived :public Base{
public:
	void print(){
		cout << "Derived" << endl;
	}
};

void doSomething(Derived* pd){
	pd->print();
}

int main(){
	Derived d;
	const Derived& crd = d;
	//doSomething(&crd);//错误,不能将const Derived*传给一个需要Derived*的函数
	doSomething(const_cast<Derived*>(&crd));//正确,&csw的常量性去除了,csw在此函数中可被更改
	doSomething((Derived*)(&crd));//正确,用了较难辨识的C旧式转型

	Base* pb = new Derived;
	//doSomething(pb);//错误,pb的静态类型是Base*,doSomething需要的是Derived*
	//doSomething(const_cast<Derived*>(pb));//错误,const只能影响常量性和变易性

	doSomething(dynamic_cast<Derived*>(pb));//正确
	//doSomething(dynamic_cast<Derived*>(&crd));//错误,不能改变常量性
	system("pause");
	return 0;
}

6、reinterpret_cast的转换结果几乎总是与编译器相关,所以它不具移植性。reinterpret_cast的最常用用途是转换“函数指针”类型。

例子:

#include<iostream>
using namespace std;

int doSomething(){
	cout << "doSomething" << endl;
}

int main(){
	typedef void(*FuncPtr)();
	FuncPtr funcPtrArray[10];
	//funcPtrArray[0] = &doSomething;//错误,类型不符
	funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);//正确

	system("pause");
	return 0;
}

函数指针的转型动作不具移植性,某些情况这样的转型可能导致不正确的结果,应尽量必变将函数指针转型。


版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/ruan875417/p/4785429.html