邪恶的C++指针

邪恶的C++指针

对于习惯PASCAL、PYTHON、JAVA等语言的程序员来说,C语言的指针很邪恶。就算是C高手,在指针上,也肯定吃了不少苦头。C++加强了类型安全,是不是指针方面的问题就会少一些呢?

现实是很残酷的,C++中指针可能造成的问题,比C只多不少。C指针会出的问题,C++中一个不落。C++还有一些C中没有的指针相关问题,有一些甚至是C++的“特产”,所有其它语言中都没有的。

  1. C++中,有构造函数的类必须使用new进行分配,否则不安全(malloc分配的必须手动调用placement new构造);

Malloc只分配内存,不负责调用构造函数。如果不调用构造函数,有些事情很难通过其它途径完成,比如,设置对象的虚函数表,构造基类对象。

  1. C++中,使用memcpy,memset操作对象指针可能造成意外错误;

C++对象中,除了数据成员,还隐藏了一些其它东西,比如虚函数表,虚拟继承当中使用到的虚基类指针。如果使用memset给C++对象赋值,就可能破坏掉这些隐藏的东西。

  1. 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类的子对象。

  1. C++中,必须使用delete释放类对象,而且要求基类有虚析构函数,否则可能造成派生类的资源泄露;

Delete会调用类的析构函数,通过析构函数释放该类拥有的资源。如果基类的析构函数不是虚函数,通过基类指针释放派生类对象时,编译器实际调用的是基类的析构函数,导致派生类拥有的资源得不到释放。