学院首页>网络编程>其它编程>C++的风格与技巧

C++的风格与技巧

作者: 来源: 添加时间:2006-5-24 13:29:58
  目录:

  我如何写这个非常简单的程序?

  为什么编译要花这么长的时间?

  为什么一个空类的大小不为0?

  我必须在类声明处赋予数据吗?

  为什么成员函数默认不是virtual的?

  为什么析构函数默认不是virtual的?

  为什么不能有虚拟构造函数?

  为什么重载在继承类中不工作?

  我能够在构造函数中调用一个虚拟函数吗?

  有没有“指定位置删除”(placement delete)?

  我能防止别人继承我自己的类吗?

  为什么不能为模板参数定义约束(constraints)?

  既然已经有了优秀的qsort()函数,为什么还需要一个sort()?

  什么是函数对象(function object)?

  我应该如何对付内存泄漏?

  我为什么在捕获一个异常之后就不能继续?

  为什么C++中没有相当于realloc()的函数?

  如何使用异常?

  怎样从输入中读取一个字符串?

  为什么C++不提供“finally”的构造?

  什么是自动指针(auto_ptr),为什么没有自动数组(auto_array)?

  可以混合使用C风格与C++风格的内存分派与重新分配吗?

  我为什么必须使用一个造型来转换*void?

  我如何定义一个类内部(in-class)的常量?

  为什么delete不会将操作数置0?

  我能够写“void main()”吗?

  为什么我不能重载点符号,::,sizeof,等等?

  怎样将一个整型值转换为一个字符串?

  “int* p”正确还是“int *p”正确?

  对于我的代码,哪一种布局风格(layout style)是最好的?

  我应该将“const”放在类型之前还是之后?

  使用宏有什么问题?



  我如何写这个非常简单的程序?



  特别是在一个学期的开始,我常常收到许多关于编写一个非常简单的程序的询问。这个问题有一个很具代表性的解决方法,那就是(在你的程序中)读入几个数字,对它们做一些处理,再把结果输出。下面是一个这样做的例子:



  #include

  #include

  #include

  using namespace std;



  int main()

  {

  vector v;



  double d;

  while(cin>>d) v.push_back(d); // 读入元素

  if (!cin.eof()) { // 检查输入是否出错

  cerr << "format error\n";

  return 1; // 返回一个错误

  }



  cout << "read " << v.size() << " elements\n";



  reverse(v.begin(),v.end());

  cout << "elements in reverse order:\n";

  for (int i = 0; i


  return 0; // 成功返回

  }



  对这段程序的观察:



  这是一段标准的ISO C++程序,使用了标准库(standard library)。标准库工具在命名空间std中声明,封装在没有.h后缀的头文件中。



  如果你要在Windows下编译它,你需要将它编译成一个“控制台程序”(console application)。记得将源文件加上.cpp后缀,否则编译器可能会以为它是一段C代码而不是C++。



  是的,main()函数返回一个int值。



  读到一个标准的向量(vector)中,可以避免在随意确定大小的缓冲中溢出的错误。读到一个数组(array)中,而不产生“简单错误”(silly error),这已经超出了一个新手的能力——如果你做到了,那你已经不是一个新手了。如果你对此表示怀疑,我建议你阅读我的文章“将标准C++作为一种新的语言来学习”("Learning Standard C++ as a New Language"),你可以在本人著作列表(my publications list)中下载到它。



  !cin.eof()是对流的格式的检查。事实上,它检查循环是否终结于发现一个end-of-file(如果不是这样,那么意味着输入没有按照给定的格式)。更多的说明,请参见你的C++教科书中的“流状态”(stream state)部分。



  vector知道它自己的大小,因此我不需要计算元素的数量。



  这段程序没有包含显式的内存管理。Vector维护一个内存中的栈,以存放它的元素。当一个vector需要更多的内存时,它会分配一些;当它不再生存时,它会释放内存。于是,使用者不需要再关心vector中元素的内存分配和释放问题。



  程序在遇到输入一个“end-of-file”时结束。如果你在UNIX平台下运行它,“end-of-file”等于键盘上的Ctrl+D。如果你在Windows平台下,那么由于一个BUG它无法辨别“end-of-file”字符,你可能倾向于使用下面这个稍稍复杂些的版本,它使用一个词“end”来表示输入已经结束。



  #include

  #include

  #include

  #include

  using namespace std;



  int main()

  {

  vector v;



  double d;

  while(cin>>d) v.push_back(d); // 读入一个元素

  if (!cin.eof()) { // 检查输入是否失败

  cin.clear(); // 清除错误状态

  string s;

  cin >> s; // 查找结束字符

  if (s != "end") {

  cerr << "format error\n";

  return 1; // 返回错误

  }

  }



  cout << "read " << v.size() << " elements\n";



  reverse(v.begin(),v.end());

  cout << "elements in reverse order:\n";

  for (int i = 0; i


  return 0; // 成功返回

  }



  更多的关于使用标准库将事情简化的例子,请参见《C++程序设计语言》中的“漫游标准库”("Tour of the Standard Library")一章。



  为什么编译要花这么长的时间?



  你的编译器可能有问题。也许它太老了,也许你安装它的时候出了错,也许你用的计算机已经是个古董。在诸如此类的问题上,我无法帮助你。



  但是,这也是很可能的:你要编译的程序设计得非常糟糕,以至于编译器不得不检查数以百计的头文件和数万行代码。理论上来说,这是可以避免的。如果这是你购买的库的设计问题,你对它无计可施(除了换一个更好的库),但你可以将你自己的代码组织得更好一些,以求得将修改代码后的重新编译工作降到最少。这样的设计会更好,更有可维护性,因为它们展示了更好的概念上的分离。



  看看这个典型的面向对象的程序例子:



  class Shape {

  public: // 使用Shapes的用户的接口

  virtual void draw() const;

  virtual void rotate(int degrees);

  // ...

  protected: // common data (for implementers of Shapes)

  Point center;

  Color col;

  // ...

  };



  class Circle : public Shape {

  public:

  void draw() const;

  void rotate(int) { }

  // ...

  protected:

  int radius;

  // ...

  };



  class Triangle : public Shape {

  public:

  void draw() const;

  void rotate(int);

  // ...

  protected:

  Point a, b, c;

  // ...

  };



  设计思想是,用户通过Shape的public接口来操纵它们,而派生类(例如Circle和Triangle)的实现部分则共享由protected成员表现的那部分实现(implementation)。



  这不是一件容易的事情:确定哪些实现部分是对所有的派生类都有用的,并将之共享出来。因此,与public接口相比,protected成员往往要做多得多的改动。举例来说,虽然理论上“中心”(center)对所有的图形都是一个有效的概念,但当你要维护一个三角形的“中心”的时候,是一件非常麻烦的事情——对于三角形,当且仅当它确实被需要的时候,计算这个中心才是有意义的。



  protected成员很可能要依赖于实现部分的细节,而Shape的用户(译注:user此处译为用户,指使用Shape类的代码,下同)却不见得必须依赖它们。举例来说,很多(大多数?)使用Shape的代码在逻辑上是与“颜色”无关的,但是由于Shape中“颜色”这个定义的存在,却可能需要一堆复杂的头文件,来结合操作系统的颜色概念。



  当protected部分发生了改变时,使用Shape的代码必须重新编译——即使只有派生类的实现部分才能够访问protected成员。



  于是,基类中的“实现相关的信息”(information helpful to implementers)对用户来说变成了象接口一样敏感的东西,它的存在导致了实现部分的不稳定,用户代码的无谓的重编译(当实现部分发生改变时),以及将头文件无节制地包含进用户代码中(因为“实现相关的信息”需要它们)。有时这被称为“脆弱的基类问题”(brittle base class problem)。


第 1 页,共 2 页 [1] [2]
站内搜索