声明(declaration)
告诉编译器关于某物的名字和类型,但它省略了某些细节。以下这些都是声明:
extern int x; // object declaration
std::size_t numDigits(int number); // function declaration
class Widget; // class declaration// template declaration
template<typename T> class GraphNode; // template declaration
注意函数 numDigits 的返回类型是 std::size_t,也就是说,namespace std 中的 size_t 类型。这个 namespace 是 C++ 标准库中每一样东西实际所在的地方。但是,因为 C 标准库(严谨地说,来自于 C89)在 C++ 中也能使用,从 C 继承来的符号(诸如 size_t)可能存在于全局范围,std 内部,或两者都有,这依赖于哪一个头文件被 #included。
size_t 仅仅是某些供 C++ 对某物计数时使用的 unsigned 类型的 typedef(例如,一个基于 char* 的 string 中字符的个数,一个 STL 容器中元素的个数,等等)。它也是 vector,deque,以及 string 的 operator[] 函数所持有的类型。
每一个函数的声明都表明了它的识别标志(signature),也就是它的参数和返回类型。一个函数的识别标志(signature)与它的类型相同。对于 numDigits 的情况,识别标志(signature)是 std::size_t (int),也就是说,“函数持有一个 int,并返回一个 std::size_t”。官方的“识别标志(signature)”的 C++ 定义排除了函数的返回类型,但是在这里中,将返回类型考虑为识别标志的一部分更加有用。
定义(definition)为编译器提供在声明时被省略的细节。对于一个对象,定义是编译器为对象留出内存的地方。对于一个函数或一个函数模板,定义提供代码本体。对于一个类或一个类模板,定义列出了类或者模板的成员:
int x; // object definition
std::size_t numDigits(int number) // function definition.
{
// (This function returns
std::size_t digitsSoFar = 1; // the number of digits in its parameter.)
while ((number /= 10) != 0) ++digitsSoFar;
return digitsSoFar;
}
class Widget { // class definition
public:
Widget();
~Widget();
...
};
template<typename T> class GraphNode // template definition
{
public:
GraphNode();
~GraphNode();
...
};
初始化(Initialization)是设定一个对象的第一个值的过程。对于用户定义类型的对象,初始化通过构造函数完成任务。缺省构造函数(default constructor)就是不需要任何引数(arguments)就可以调用的构造函数。这样的一个构造函数既可以是没有参数(parameters),也可以是每一个参数都有缺省值:
class A {
public:
A(); // default constructor
};
class B {
public:
explicit B(int x = 0, bool b = true); // default constructor; see below
}; // for info on "explicit"
class C {
public:
explicit C(int x); // not a default constructor
};
这里 B 和 C 的构造函数都被声明为 explicit(显式的)。这是为了防止它们被用来执行隐式类型转换(implicit type conversions),虽然他们还可以被用于显示类型转换(explicit type conversions):
void doSomething(B bObject); // a function taking an object of type B
B bObj1; // an object of type B
doSomething(bObj1); // fine, passes a B to doSomething
B bObj2(28); // fine, creates a B from the int 28(the bool defaults to true)
doSomething(28); // error! doSomething takes a B,
// not an int, and there is no implicit conversion from int to B
doSomething(B(28)); // fine, uses the B constructor to
// explicitly convert (i.e., cast) the int to a B for this call.
构造函数被声明为 explicit(显式的)通常比 non-explicit(非显式)的更可取,因为它们可以防止编译器执行意外的(常常是无意识的)类型转换。除非有一个好的理由允许一个构造函数被用于隐式类型转换(implicit type conversions),否则就将它声明为 explicit(显式的)。
拷贝构造函数(copy constructor)被用来以一个对象来初始化同类型的另一个对象,拷贝赋值运算符(copy assignment operator)被用来将一个对象中的值拷贝到同类型的另一个对象中:
class Widget {
public:
Widget(); // default constructor
Widget(const Widget& rhs); // copy constructor
Widget& operator=(const Widget& rhs); // copy assignment operator
...
};
Widget w1; // invoke default constructor
Widget w2(w1); // invoke copy constructor
w1 = w2; // invoke copy assignment operator
当看到什么东西看起来像一个赋值的话,要仔细阅读,因为 "=" 在语法上还可以被用来调用拷贝构造函数:
Widget w3 = w2; // invoke copy constructor!
拷贝构造函数很容易从拷贝赋值中区别出来。如果一个新的对象被定义(就象上面那行代码中的 w3),一个构造函数必须被调用;它不可能是一个赋值。如果没有新的对象被定义(就象上面那行 "w1 = w2" 代码中),没有构造函数能被调用,所以它就是一个赋值。
拷贝构造函数是一个特别重要的函数,因为它定义一个对象如何通过传值的方式被传递。例如,考虑这个:
bool hasAcceptableQuality(Widget w);
...
Widget aWidget;
if (hasAcceptableQuality(aWidget)) ...
参数 w 通过传值的方式被传递给 hasAcceptableQuality,所以在上面的调用中,aWidget 被拷贝给 w。拷贝动作通过 Widget 的拷贝构造函数被执行。通过传值方式传递意味着“调用拷贝构造函数”。(无论如何,通过传值方式传递用户定义类型通常是一个不好的想法,传引用给 const 通常是更好的选择。)
STL 是标准模板库(Standard Template Library),作为 C++ 的标准库的一部分,致力于容器(containers)(例如,vector,list,set,map,等等),迭代器(iterators)(例如,vector<int>::iterator,set<string>::iterator,等等),算法(algorithms)(例如,for_each,find,sort,等等),以及相关机能。相关机能中的很多都通过函数对象(function objects)——行为表现类似于函数的对象——提供。这样的对象来自于重载了 operator() ——函数调用运算符——的类。
从 Java 或 C# 那样的语言来到 C++ 的程序员可能会对未定义行为(undefined behavior)的概念感到吃惊。因为各种各样的原因,C++ 中的一些结构成分(constructs)的行为没有确切的定义:我们不能可靠地预知运行时会发生什么。这里是两个带有未定义行为的代码的例子:
int *p = 0; // p is a null pointer
std::cout << *p; // dereferencing a null pointer yields undefined behavior
char name[] = "Darla"; // name is an array of size 6 (don’t
// forget the trailing null!)
char c = name[10]; // referring to an invalid array index yields undefined behavior
为了强调未定义行为的结果是不可预言而且可能是令人讨厌的,有经验的 C++ 程序员常常说带有未定义行为的程序能(can)删除我们的硬盘。这是真的:一个带有未定义行为的程序可以(could)删除我们的硬盘。只不过可能性不太大。更可能的是那个程序的表现反复无常,有时会运行正常,有时会彻底崩溃,还有时会产生错误的结果。有实力的 C++ 程序员能以最佳状态避开未定义行为。。
另一个可能把从其它语言转到 C++ 的程序员搞糊涂的条目是接口(interface)。Java 和 .NET 的语言都将接口作为一种语言要素,但是在 C++ 中没有这种事。当我们使用条目“接口(interface)”时,一般情况下说的是一个函数的识别标志,是一个类的可访问元素(例如,一个类的 "public interface","protected interface",或 "private interface"),或者是对一个模板的类型参数来说必须合法的表达式。也就是说,我们是作为一个相当普遍的设计概念来谈论接口(interface)的。
客户(client)是使用我们写的代码(一般是接口(interfaces))的某人或某物。例如,一个函数的客户就是它的用户:调用这个函数(或持有它的地址)的代码的片段以及写出和维护这样的代码的人。类或者模板的客户是使用这个类或模板的软件的部件,以及写出和维护那些代码的程序员。
TR1 and Boost TR1 和 BoostTR1 ("Technical Report 1") 是被加入 C++ 标准库的新机能的规格说明书。这些机能以新的类和函数模板的形式提供了诸如哈希表(hash tables),引用计数智能指针(reference-counting smart pointers),正则表达式(regular expressions),等等。所有的 TR1 组件都位于嵌套在 namespace std 内部的 namespace tr1 内。
·Boost 是一个组织和一个网站提供的可移植的,经过同行评审的,开源的 C++ 库。大多数 TR1 机能都基于 Boost 的工作,而且直到编译器厂商在他们的 C++ 库发行版中包含 TR1 之前,Boost 网站很可能会保持开发者寻找 TR1 实现的第一站的地位。Boost 提供的东西比用于 TR1 的更多,无论如何,在很多情况下,它还是值得去了解一下的。 (http://boost.org)
extern int x; // object declaration
std::size_t numDigits(int number); // function declaration
class Widget; // class declaration// template declaration
template<typename T> class GraphNode; // template declaration
注意函数 numDigits 的返回类型是 std::size_t,也就是说,namespace std 中的 size_t 类型。这个 namespace 是 C++ 标准库中每一样东西实际所在的地方。但是,因为 C 标准库(严谨地说,来自于 C89)在 C++ 中也能使用,从 C 继承来的符号(诸如 size_t)可能存在于全局范围,std 内部,或两者都有,这依赖于哪一个头文件被 #included。
size_t 仅仅是某些供 C++ 对某物计数时使用的 unsigned 类型的 typedef(例如,一个基于 char* 的 string 中字符的个数,一个 STL 容器中元素的个数,等等)。它也是 vector,deque,以及 string 的 operator[] 函数所持有的类型。
每一个函数的声明都表明了它的识别标志(signature),也就是它的参数和返回类型。一个函数的识别标志(signature)与它的类型相同。对于 numDigits 的情况,识别标志(signature)是 std::size_t (int),也就是说,“函数持有一个 int,并返回一个 std::size_t”。官方的“识别标志(signature)”的 C++ 定义排除了函数的返回类型,但是在这里中,将返回类型考虑为识别标志的一部分更加有用。
定义(definition)为编译器提供在声明时被省略的细节。对于一个对象,定义是编译器为对象留出内存的地方。对于一个函数或一个函数模板,定义提供代码本体。对于一个类或一个类模板,定义列出了类或者模板的成员:
int x; // object definition
std::size_t numDigits(int number) // function definition.
{
// (This function returns
std::size_t digitsSoFar = 1; // the number of digits in its parameter.)
while ((number /= 10) != 0) ++digitsSoFar;
return digitsSoFar;
}
class Widget { // class definition
public:
Widget();
~Widget();
...
};
template<typename T> class GraphNode // template definition
{
public:
GraphNode();
~GraphNode();
...
};
初始化(Initialization)是设定一个对象的第一个值的过程。对于用户定义类型的对象,初始化通过构造函数完成任务。缺省构造函数(default constructor)就是不需要任何引数(arguments)就可以调用的构造函数。这样的一个构造函数既可以是没有参数(parameters),也可以是每一个参数都有缺省值:
class A {
public:
A(); // default constructor
};
class B {
public:
explicit B(int x = 0, bool b = true); // default constructor; see below
}; // for info on "explicit"
class C {
public:
explicit C(int x); // not a default constructor
};
这里 B 和 C 的构造函数都被声明为 explicit(显式的)。这是为了防止它们被用来执行隐式类型转换(implicit type conversions),虽然他们还可以被用于显示类型转换(explicit type conversions):
void doSomething(B bObject); // a function taking an object of type B
B bObj1; // an object of type B
doSomething(bObj1); // fine, passes a B to doSomething
B bObj2(28); // fine, creates a B from the int 28(the bool defaults to true)
doSomething(28); // error! doSomething takes a B,
// not an int, and there is no implicit conversion from int to B
doSomething(B(28)); // fine, uses the B constructor to
// explicitly convert (i.e., cast) the int to a B for this call.
构造函数被声明为 explicit(显式的)通常比 non-explicit(非显式)的更可取,因为它们可以防止编译器执行意外的(常常是无意识的)类型转换。除非有一个好的理由允许一个构造函数被用于隐式类型转换(implicit type conversions),否则就将它声明为 explicit(显式的)。
拷贝构造函数(copy constructor)被用来以一个对象来初始化同类型的另一个对象,拷贝赋值运算符(copy assignment operator)被用来将一个对象中的值拷贝到同类型的另一个对象中:
class Widget {
public:
Widget(); // default constructor
Widget(const Widget& rhs); // copy constructor
Widget& operator=(const Widget& rhs); // copy assignment operator
...
};
Widget w1; // invoke default constructor
Widget w2(w1); // invoke copy constructor
w1 = w2; // invoke copy assignment operator
当看到什么东西看起来像一个赋值的话,要仔细阅读,因为 "=" 在语法上还可以被用来调用拷贝构造函数:
Widget w3 = w2; // invoke copy constructor!
拷贝构造函数很容易从拷贝赋值中区别出来。如果一个新的对象被定义(就象上面那行代码中的 w3),一个构造函数必须被调用;它不可能是一个赋值。如果没有新的对象被定义(就象上面那行 "w1 = w2" 代码中),没有构造函数能被调用,所以它就是一个赋值。
拷贝构造函数是一个特别重要的函数,因为它定义一个对象如何通过传值的方式被传递。例如,考虑这个:
bool hasAcceptableQuality(Widget w);
...
Widget aWidget;
if (hasAcceptableQuality(aWidget)) ...
参数 w 通过传值的方式被传递给 hasAcceptableQuality,所以在上面的调用中,aWidget 被拷贝给 w。拷贝动作通过 Widget 的拷贝构造函数被执行。通过传值方式传递意味着“调用拷贝构造函数”。(无论如何,通过传值方式传递用户定义类型通常是一个不好的想法,传引用给 const 通常是更好的选择。)
STL 是标准模板库(Standard Template Library),作为 C++ 的标准库的一部分,致力于容器(containers)(例如,vector,list,set,map,等等),迭代器(iterators)(例如,vector<int>::iterator,set<string>::iterator,等等),算法(algorithms)(例如,for_each,find,sort,等等),以及相关机能。相关机能中的很多都通过函数对象(function objects)——行为表现类似于函数的对象——提供。这样的对象来自于重载了 operator() ——函数调用运算符——的类。
从 Java 或 C# 那样的语言来到 C++ 的程序员可能会对未定义行为(undefined behavior)的概念感到吃惊。因为各种各样的原因,C++ 中的一些结构成分(constructs)的行为没有确切的定义:我们不能可靠地预知运行时会发生什么。这里是两个带有未定义行为的代码的例子:
int *p = 0; // p is a null pointer
std::cout << *p; // dereferencing a null pointer yields undefined behavior
char name[] = "Darla"; // name is an array of size 6 (don’t
// forget the trailing null!)
char c = name[10]; // referring to an invalid array index yields undefined behavior
为了强调未定义行为的结果是不可预言而且可能是令人讨厌的,有经验的 C++ 程序员常常说带有未定义行为的程序能(can)删除我们的硬盘。这是真的:一个带有未定义行为的程序可以(could)删除我们的硬盘。只不过可能性不太大。更可能的是那个程序的表现反复无常,有时会运行正常,有时会彻底崩溃,还有时会产生错误的结果。有实力的 C++ 程序员能以最佳状态避开未定义行为。。
另一个可能把从其它语言转到 C++ 的程序员搞糊涂的条目是接口(interface)。Java 和 .NET 的语言都将接口作为一种语言要素,但是在 C++ 中没有这种事。当我们使用条目“接口(interface)”时,一般情况下说的是一个函数的识别标志,是一个类的可访问元素(例如,一个类的 "public interface","protected interface",或 "private interface"),或者是对一个模板的类型参数来说必须合法的表达式。也就是说,我们是作为一个相当普遍的设计概念来谈论接口(interface)的。
客户(client)是使用我们写的代码(一般是接口(interfaces))的某人或某物。例如,一个函数的客户就是它的用户:调用这个函数(或持有它的地址)的代码的片段以及写出和维护这样的代码的人。类或者模板的客户是使用这个类或模板的软件的部件,以及写出和维护那些代码的程序员。
TR1 and Boost TR1 和 BoostTR1 ("Technical Report 1") 是被加入 C++ 标准库的新机能的规格说明书。这些机能以新的类和函数模板的形式提供了诸如哈希表(hash tables),引用计数智能指针(reference-counting smart pointers),正则表达式(regular expressions),等等。所有的 TR1 组件都位于嵌套在 namespace std 内部的 namespace tr1 内。
·Boost 是一个组织和一个网站提供的可移植的,经过同行评审的,开源的 C++ 库。大多数 TR1 机能都基于 Boost 的工作,而且直到编译器厂商在他们的 C++ 库发行版中包含 TR1 之前,Boost 网站很可能会保持开发者寻找 TR1 实现的第一站的地位。Boost 提供的东西比用于 TR1 的更多,无论如何,在很多情况下,它还是值得去了解一下的。 (http://boost.org)