邪恶的C++指针
邪恶的C++指针
对于习惯PASCAL、PYTHON、JAVA等语言的程序员来说,C语言的指针很邪恶。就算是C高手,在指针上,也肯定吃了不少苦头。C++加强了类型安全,是不是指针方面的问题就会少一些呢?
现实是很残酷的,C++中指针可能造成的问题,比C只多不少。C指针会出的问题,C++中一个不落。C++还有一些C中没有的指针相关问题,有一些甚至是C++的“特产”,所有其它语言中都没有的。
- C++中,有构造函数的类必须使用new进行分配,否则不安全(malloc分配的必须手动调用placement new构造);
Malloc只分配内存,不负责调用构造函数。如果不调用构造函数,有些事情很难通过其它途径完成,比如,设置对象的虚函数表,构造基类对象。
- C++中,使用memcpy,memset操作对象指针可能造成意外错误;
C++对象中,除了数据成员,还隐藏了一些其它东西,比如虚函数表,虚拟继承当中使用到的虚基类指针。如果使用memset给C++对象赋值,就可能破坏掉这些隐藏的东西。
- C++中,如果存在多继承,指针类型转换可能导致指针值发生改变,当只有前置声明存在时,转换会有错误;
以下述代码为例:
第一种情况
Class C : public A, B {};
C c;
B* b = (B*)&c;
A* a = (A*)&c;
第二种情况
Calss A;
Class B;
Class C;
Void fun(C* c)
{
B* b = (B*)c;
A* a = (A*)c;
}
类C继承了两个基类A和B,C对象中分别包含了一个A和B的子对象。A和B两个基类的子对象先后存放,必然不在同一个地址上,C对象的地址如果和A相同就必然和B不相同。如“第一种情况”所述,如果有C的定义,C++编译器知道A和B的两个子对象相对C的偏移,能正确转换,转换得到的a和b必然不相同。如“第二种情况”所述,如果只有A、B、C的前向声明,C++编译器就只能采用C语言的转换方式,即,指针不产生任何偏移,得到的a和b相同,都指向了A类的子对象。
- C++中,必须使用delete释放类对象,而且要求基类有虚析构函数,否则可能造成派生类的资源泄露;
Delete会调用类的析构函数,通过析构函数释放该类拥有的资源。如果基类的析构函数不是虚函数,通过基类指针释放派生类对象时,编译器实际调用的是基类的析构函数,导致派生类拥有的资源得不到释放。