C++可以做好而C做不好的事情

导读

现在好多C++软件工程师,大多数都是C语言出身,他们在工作过程中用得更多的还是C语言思想,或许他们还没有意识到C++ 的一些更加有趣的特性。现在给大家解释什么事情是C++可以做好,而C做不好的。希望对这一类人有一点点启发。

目录

  1. 第一次尝试
  2. 改进
  3. 另一种改进
  4. 不用类来改进

正文

1、第一次尝试

现在给定一个例子:

#include <stdio.h>
class Trace{
public:
	void print( char *s ){ printf("%s", s); }
};
int main(){
	Trace t;
	t.print("begin main
");
	t.print("end main
");
}

2、改进

如果能够在必要时关闭跟踪输出(trace output,这将会是个有用的功能。现在改一下类的定义就行:

#include <stdio.h>
class Trace{
public:
	Trace(){ noisy = 0; }
	void print( char *s ){ if (noisy)  printf("%s", s); }
	void on() { noisy = 1; }
	void off() { noisy = 0; }
private:
	int noisy;
};

此时类定义包括两个公有成员函数onoff,它们影响私有成员noisy的状态。只有noisyon(非零)才可以输出。因此,

t.off();

会关闭t的对外输出,直到我们通过下面的语句恢复t的输出能力:

t.on();

由于这些成员函数定义在Trace类自身的定义内,C++会内联(inline)扩展它们,所以就使得即使在不进行跟踪的情况下,在程序中保留Trace对象也不必付出许多代价。只要让print函数不做任何事情,然后重新编译程序,就可以有效地关闭所有Trace对象的输出。

3、另一种改进

如果让它打印到标准输出设备以外的东西上,那又该怎么改进呢?一种方法是可以用继承来创建一种新的Trace类,但是为了尽量让示例简单,避免介绍新的概念。现在作如下改进:

#include <stdio.h>
class Trace{
public:
	Trace(){ noisy = 0; f = stdout; }
	Trace(FILE* ff){ noisy = 0; f = ff; }
	void print( char *s ){ if (noisy) fprintf(f, "%s", s); }
	void on() { noisy = 1; }
	void off() { noisy = 0; }
private:
	int noisy;
	FILE* f;
};

这样的改动基于一个事实:

printf(args);

等价于:

fprintf(stdout, args);

创建一个没有特殊要求的Trace类,则其对象的成员fstdout。因此,调用fprintf所做的工作与调用前一个版本的printf是一样的。

Trace有两个构造函数:一个是无参的构造函数,输出到stdout,另一个是带参构造函数,允许明确指定输出文件。因此,上面那个使用了Trace类的示例程序可以继续工作,但也可

以将输出定向到比如说stderr上:

int main(){
	Trace t(stderr);
	t.print("begin main
");
	t.print("end main
");
}

简而言之,运用C++类的特殊方式,使得对程序的改进变得轻而易举,而不会影响使用这些类的代码。

4、不用类来实现

对于这个问题,典型的C解决方案会怎么样的。

#include <stdio.h>
static int noisy = 1;
void trace(char *s)
{
	if(noisy)
		printf("%s
", s);
}

void trace_on(){ noisy = 1; }
void trace_off(){ noisy = 0;}

这个方法是有效的,但是与C++方法比较起来,有3个明显的缺点。

1. 函数trace不是内联,因此即使当跟踪关闭时,它还保持着函数调用的开销。

2. C版本引入了3个全局名字:tracetrace_ontrace_off,而C++只引入了1个。

3. 很难将这个例子一般化,使之能输出到一个以上的文件中。为什么这么说呢?考虑一下我们会怎么样使用这个trace函数:

int main(){
	trace("begin main
");
	// main函数主体
	trace ("end main
");
}

采用C++,可以只在创建Trace对象时一次性指定文件名。而在C版本中,情况相反,没有合适的位置指定文件名。一个显而易见的办法就是给函数trace 增加一个参数,但是需要找到所有对trace函数的调用,并插入这个新增的参数。另一种办法就是引入名为trace_out的第4个函数,用来将跟踪输出转向到其他文件。这当然也得要求判断和记录跟踪输出是打开还是关闭。考虑一下,譬如,main调用的一个函数恰好利用了trace_out向另一个文件输出,则何时切换输出的开关状态呢?显然,要想使结果正确需要花费相当的精力。

原文地址:https://www.cnblogs.com/iyoyos/p/4284917.html