学习程序语言根本大法是一回事,学习如何以某种语言设计并实现高效程序则是另外一回事。css
一组明智选择并精心设计的classes、functions、templates可以使程序编写容易、直观、高效、而且远离错误。git
带着问题去品读这本经验著做:程序员
设计上的讨论:
“如何在两个不一样的作法中择一完成某项任务?”数组
即便彻底知道该作什么,彻底进入正轨可能仍是可能有点棘手。函数
榨出这些细节很重要,本书将带你趋凶避吉,避免那些未可预期、神秘难解的程序行为。学习
软件设计和实现是复杂的差事,被硬件、操做系统、应用程序的约束条件涂上五光十色,因此我能作的最好的就是提供指南,让你得以创造出更棒的程序。ui
准则天生就带有例外。这就是为何每一个条款都有解释与说明。这些解释与说明是本书最重要的一部分。惟有了解条款背后的基本原理,你才可以决定是否将它套用于你所开发的软件,并奉行其所昭示的独特约束。spa
本书的最佳用途:操作系统
本书的最佳用途就是完全了解 C++ 如何行为、为何那样行为,以及如何运用其行为造成优点。设计
下面是每一个C++程序员都应该了解的一份小小的 C++ 词汇。
声明式
所谓声明式(declaration)是告诉编译器某个东西的名称和类型,可是略去细节。
extern int x; //对象声明式
std::size_t numDigits(int number); //函数声明式
class Widget; //类声明式
template<typename T> //模板声明式
class GraphNode;
签名式
每一个函数的声明揭示其签名式(signature),也就是参数和返回值类型。一个函数的签名就等同于该函数的类型。numDigits函数的签名是std::size_t (int),也就是说“这个函数得到一个int 并返回一个 std::size_t”。
定义式(definition)
定义式的任务是提供编译器一些声明式所遗漏的细节。对对象而言,定义式是编译器为此对象拨发内存的地点。
对function 或者 function template 而言,定义式提供了代码本体。对 class 或者 class template 而言,定义式列出它的成员:
int x; //对象定义式
std::size_t numDigits(int number) //函数定义式
{
//返回其参数的数字个数
std::size_t digitsSoFar = 1;
while ((number /= 10) != 0)
++ digitsSoFar;
return digitsSoFar;
}
class Widget //class 定义式
{
public:
Widget();
~Widget();
...
};
template<typename T> //template 的定义式
class GraphNode{
public:
GraphNode();
~GraphNode();
...
};
初始化(initialization)
初始化(initialization)是“给予对象初值”的过程。对用户自定义类型的对象而言,初始化由构造函数执行。
所谓default构造函数是一个可被调用而不带任何实参者。这样的构造函数
要不没有参数,要不就是每一个参数都有缺省值:
class A{
public:
A(); //default构造函数
};
class B{
public:
explicit B(int x = 0, bool b = true); //default构造函数
}
class C{
public:
explicit C(int x); //不是default构造函数
}
上述的class B 和 C的构造函数都被声明为 explicit,这可阻止它们被用来执行隐式类型转换(implicit type conversions),但他们仍可被用来进行显示类型转换(explicit type conversions):
void doSomething(B bobject); //此函数接受一个类型为B的对象
B bObj1; //一个类型为B的对象
doSomething(bObj1); //没问题,传递一个B给
//doSomething函数
B bObj2(28); //没问题,根据int 28 创建一个B
//(函数的bool参数缺省为true)
doSomething(28); //错误!doSomething 应该接受
//一个B,不是一个 int ,
//int 和 B 之间没有隐式转换
doSomething(B(28)); //没问题,使用 B 构造函数将 int
//显式转换为一个 B 以促成此调用
被声明成 explicit 的构造函数禁止编译器执行非预期的类型转换。除非你有一个好理由容许构造函数被用于隐式类型转换,不然你应该把他声明为 explicit 。
copy构造函数 和 copy assignment操做符
copy构造函数被用来”以同类型对象初始化自我对象”
copy assignment 操做符被用来“从另外一个同类型对象中拷贝其值到自我对象”:
class Widget{
public:
Widget(); //default构造函数
Widget(const Widget& rhs); //copy构造函数
Widget& operator=(const Widget& rhs); //copy assignment操做符
...
};
Widget w1; //调用default构造函数
Widget w2(w1); //调用copy构造函数
w1 = w2; //调用 copy assignment操做符
当你看到赋值符号时请当心,由于“=”语法也可用来调用copy构造函数:
Widget w3 = w2; //调用copy构造函数
如何区别是”copy构造”仍是”copy赋值”:
若是一个新对象被定义,必定会有一个构造函数被调用,不可能调用赋值操做。若是没有新对象被定义(例如前面的 “w1 = w2”语句),就不会有构造函数被调用,那么固然就是赋值操做被调用。
copy构造函数是一个尤为重要的函数,由于他定义了一个对象如何 pass-by-value(以值传递)。
举个例子:
bool hasAcceptableQuality(Widget w);
...
Widget aWidget;
if (hasAcceptableQuality(aWidget)) ...
参数w是以 by value 方式传递给 hasAcceptableQuality,因此在上述调用中 aWidget被复制到 w 体内。这个复制动做由 Widget 的copy构造函数完成。pass-by-value 意味“调用 copy 构造函数”。以 by value 传递用户自定义类型一般是个坏主意, Pass-by-Reference-to-const每每是比较好的选择。
未定义行为(Undefined behavior)[不明确行为]
int* p = 0; //p是一个null指针
std::cout << *p; //对一个null指针取值会致使不明确的行为
//null指针可读不可写
char name[] = "Darla"; //name 是个数组,大小为6(别忘记最尾端的null)
char c = name[10]; //只涉及一个无效的数组索引