电脑爱好者,提供IT资讯信息及各类编程知识文章介绍,欢迎大家来本站学习电脑知识。 最近更新 | 联系我们 RSS订阅本站最新文章
电脑爱好者
站内搜索: 
当前位置:首页>> C++/VC>>C++箴言:理解Terminology术语之一:

C++箴言:理解Terminology术语之一

来源:远方网络 | 2006-1-6 | (有1884人读过)

这是一个所有程序员都应该了解的小型的 C++ 词汇表。下面的条目都足够重要,值得我们对它们的含义务必取得完全一致。

  声明(declaration)告诉编译器关于某物的名字和类型,但它省略了某些细节。以下这些都是声明:


extern int x; // object declaration

std::size_t numDigits(int number); // function declaration

class Widget; // class declaration

template<typename T> // template declaration
class GraphNode; // (see Item 42 for info on
// the use of "typename")

  注意即使是内建类型,我还是更喜欢将整数看作一个 "object",某些人将 "object" 这个名字保留给用户定义类型,但我不是他们中的一员。再有就是注意函数 numDigits 的返回类型是 std::size_t,也就是说,namespace std 中的 size_t 类型。这个 namespace 是 C++ 标准库中每一样东西实际所在的地方。但是,因为 C 标准库(严谨地说,来自于 C89)在 C++ 中也能使用,从 C 继承来的符号(诸如 size_t)可能存在于全局范围,std 内部,或两者都有,这依赖于哪一个头文件被 #included。在本书中,我假设 C++ 头文件被 #included,这也就是为什么我用 std::size_t 代替 size_t 的原因。当行文中涉及到标准库组件时,我一般不再提及 std,这依赖于你认可类似 size_t,vector,以及 cout 之类的东西都在 std 中,在示例代码中,我总是包含 std,因为真正的代码没有它将无法编译。

  顺便说一下,size_t 仅仅是某些供 C++ 对某物计数时使用的 unsigned 类型的 typedef(例如,一个基于 char* 的 string 中字符的个数,一个 STL 容器中元素的个数,等等)。它也是 vector,deque,以及 string 的 operator[] 函数所持有的类型,这是一个在 Item 3 中定义我们自己的 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> // template definition
class GraphNode {
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. (See
// Item 27 for info on casting.)

  构造函数被声明为 explicit(显式的)通常比 non-explicit(非显式)的更可取,因为它们可以防止编译器执行意外的(常常是无意识的)类型转换。除非我有一个好的理由允许一个构造函数被用于隐式类型转换(implicit type conversions),否则就将它声明为 explicit(显式的)。我希望你能遵循同样的方针。

  构造函数被声明为 explicit(显式的)通常比 non-explicit(非显式)的更可取,因为它们可以防止编译器执行意外的(常常是无意识的)类型转换。除非我有一个好的理由允许一个构造函数被用于隐式类型转换(implicit type conversions),否则就将它声明为 explicit(显式的)。我希望你能遵循同样的方针。

  请注意我是如何突出上面的示例代码中的强制转换(cast)的。贯穿本书,我用这样的突出引导你注意那些应该注意的材料。(我也突出章节号码,但那仅仅是因为我想让它好看一些。)

  拷贝构造函数(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 通常是更好的选择。)
C++/VC热门文章排行
网站赞助商
购买此位置

 

关于我们 | 网站地图 | 文档一览 | 友情链接| 联系我们

Copyright © 2003-2024 电脑爱好者 版权所有 备案号:鲁ICP备09059398号