看这样一段代码。
- #include <iostream>
- usingnamespace std;
- class A{
- public:
- virtualvoid f(){cout<<"A"<<endl;}
- };
- class B:public A
- {
- private:
- void f(){cout<<"B"<<endl;}
- };
- int main()
- {
- A* pA = new B();
- pA->f();
- return 0;
- }
出现了很有趣的结果,在main函数中,访问到了B类中的私有方法。为什么会出现这样的结果呢?
一般来说外部对象访问类的私有成员,除非是友元,否则在编译的时候就会报错,但是上面那段代码却可以正常的编译通过。
C++因为支持面向对象编程,制订了一系列实现策略的语言机制。其中,各种各样的“限制”主要是出现在编译时:因此如果直接B d; d.f(); 就会导致编译错误:编译器发现 B::f()是B类的私有成员函数,因此拒绝这样的访问。
这里我们可以区分类和对象:类是编译期的概念,也是“访问权限”、“成员数据”、“成员函数”这几个概念的“作用域”。而对象的作用域是运行期。它包括类的实例、引用和指针。 A *pA = new B(); 这里 pA 是一个 A* , 所以就作为一个A类的指针参与了编译;因此从pA调用f()在编译器眼中,就是调用了A类的公开成员函数f()因此通过编译;然后在运行时,由于多态作用pA调用的f()是派生类的f()成员函数。虽然这时f()是private成员函数,但是由于 private/public 这些访问控制是编译时的限制,在运行时无效,所以B::f() 被成功调用。如果能够理解这两个在不同时间作用的概念,这个问题就很好理解.
由这个问题,就引出了编译期和运行期的概念。
顾名思义,编译期指的是编译器在编译代码的时候,而运行期指的是程序运行的时候。
在大型系统中,log信息的管理是一项十分庞大的工程。通常来说log分为两种,一种是调试的log信息,一种是出错的Log信息,前者只对本模块的开发工程师可见,后者本系统的工程师和测试工程师可见。由此就引入了log信息的管理,我们通常有release版本和debug版本,release版本的log信息只限于出错log,debug则输出本模块的log。闲话扯远了,回归正题。
如何来控制log信息的输出,也就是程序如何判断是否输出log。很明显我们需要判断语句,那么如何使用判断语句才能保证最好的效果。这里有两种方法。
设定全局const变量,在打印函数中根据全局变量来判断。这样的实现也很简洁,修改起来也很方便。
const bool g_debug 1
void Log(char* str)
{
if (g_debug){
printf(str);
}
}
还有一种方法,利用宏来控制Log的输出,用全局宏代替全局变量,宏函数代替全局函数。
#define DEBUG
#ifdef DEBUG
#define LOG(m) /
{/
printf(m);/
}/
#else
#define LOG(m)
#endif
有经验的程序员肯定一眼就看出来第二种Log输出方式比较好。第一种的判断是在运行期,而第二种的判断是在编译期。第一种在每次运行log函数的时候都会去判断全局变量,而第二种是在编译的时候就已经将不需要的Log信息定义为空。因为编译消耗的时间是一次性的,而运行的效率是直接呈现给用户的。由这种节约时间的方法,我们可以发现把很多函数的判断和计算放在编译期,会大大节约运行的时间,提供运行效率,但是编译器并不是万能,所以编译期能做的工作还是十分的有限。