c++ - 小心从派生类到基类的转换 (ptr Vs. obj)
2011-05-12 15:04
363 查看
将一个派生类指针或引用赋给基类指针或引用是安全的且自动转换的;将一个派生类对象赋给基类对象将发生“切割”(sliced),是不正确的写法!
由于Son对象包含了一个Base对象,因此用一个Son对象去初始化Base对象时,实际上是将自身的Base类部分拷贝构造新的Base对象,而新的Base对象没有Son类成员的存储空间,因此将Son类的成员丢弃,即“切割”。此外,虽然新的Base对象已经copy了Son对象的Base这部分数据,但是虚表并没有更新(还是Son的虚表)。也就是说,这里有两个大隐患。
相反,将一个Son对象的指针赋给Base类的指针时,只是把Son对象的地址拷贝给一个指针类型的变量,并没有发生对象拷贝操作或者改变原来的对象。
看看下面的例子
void FunPtr(Base*);
void FunVal(Base );
//...
Base* basePtr = new Base();
Son * sonPtr = new Son();
//...
FunPtr( sonPtr); //No problem
FunPtr( basePtr); //NP
FunVal(*sonPtr); //wrong: sliced!!!
FunVal(*basePtr); //NP from the view of syntax, but has a performance problem
//for passing parameters by values,it will trigger a copy constructor
//...
Base base(*sonPtr); //Wrong: sliced!!!
Son son;
//...
*basePtr = son; //Wrong: sliced!!!
从上面的例子可知,如果在函数的参数列表中使用pass by value,要注意避免使用有继承关系的类;通常只有build in类型或者structure类型才不会有本文中的问题。
除了自己留意,避免这种错误外,有没有根本的解决之道呢?
如果一个类可以被继承,完全可以根据“按接口编程”的思想,提取一个接口;这在c++中就是纯虚基类。如果没必要纯虚,也可以把基类作为一个抽象类。因为抽象类不能实例化,那么上面的3处错误可以在编译期就被发现,而不是运行态:借用一句名言,能被编译器发现的问题不是问题!
扩展开来讲,具体类不适合作为基类,抽象类更合理。这个不仅仅有设计上的必要,也有实践上的意义(就是本文中所述的)
由于Son对象包含了一个Base对象,因此用一个Son对象去初始化Base对象时,实际上是将自身的Base类部分拷贝构造新的Base对象,而新的Base对象没有Son类成员的存储空间,因此将Son类的成员丢弃,即“切割”。此外,虽然新的Base对象已经copy了Son对象的Base这部分数据,但是虚表并没有更新(还是Son的虚表)。也就是说,这里有两个大隐患。
相反,将一个Son对象的指针赋给Base类的指针时,只是把Son对象的地址拷贝给一个指针类型的变量,并没有发生对象拷贝操作或者改变原来的对象。
看看下面的例子
void FunPtr(Base*);
void FunVal(Base );
//...
Base* basePtr = new Base();
Son * sonPtr = new Son();
//...
FunPtr( sonPtr); //No problem
FunPtr( basePtr); //NP
FunVal(*sonPtr); //wrong: sliced!!!
FunVal(*basePtr); //NP from the view of syntax, but has a performance problem
//for passing parameters by values,it will trigger a copy constructor
//...
Base base(*sonPtr); //Wrong: sliced!!!
Son son;
//...
*basePtr = son; //Wrong: sliced!!!
从上面的例子可知,如果在函数的参数列表中使用pass by value,要注意避免使用有继承关系的类;通常只有build in类型或者structure类型才不会有本文中的问题。
除了自己留意,避免这种错误外,有没有根本的解决之道呢?
如果一个类可以被继承,完全可以根据“按接口编程”的思想,提取一个接口;这在c++中就是纯虚基类。如果没必要纯虚,也可以把基类作为一个抽象类。因为抽象类不能实例化,那么上面的3处错误可以在编译期就被发现,而不是运行态:借用一句名言,能被编译器发现的问题不是问题!
扩展开来讲,具体类不适合作为基类,抽象类更合理。这个不仅仅有设计上的必要,也有实践上的意义(就是本文中所述的)
相关文章推荐
- C++中派生类和基类的转换和访问控制
- C++基类与派生类的转换与多态性
- 转:c++ 基类转换为派生类
- C++基类与派生类的转换
- C++中基类和派生类之间的转换实例教程
- C++ 基类 派生类 互相转换 调用关系
- C++基类与派生类的转换
- C++派生类与基类的转换规则
- c++派生类转换为基类与public、protected、private继承的关系
- C++ 从基类到派生类的转换
- C++基类与派生类的转换
- C++私有继承派生类转换成基类引用实例
- C++中基类对象安全转换为派生类对象的方法
- C++ - 派生类强制转换为基类
- 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换
- 命名白白c++ 基类和派生类的转换
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
- c++ 初学 派生类到基类转换的可访问性
- C++基类与派生类的转换
- c++——派生类和基类转换