c++子类和父类指针的转换

本文探讨了C++中父类指针如何安全地调用子类新增的方法。通过两种不同的转换方式——不安全的强制类型转换与安全的动态类型转换,解释了各自的原理及应用场景。

1.父类指针无法直接调用子类的新函数,需要转换为子类指针后才能调用。

c++编译器在编译的时候是做静态类型分析,父类指针是否真的指向一个子类类型,编译器并不会做这个假设。因此用父类的指针去调用子类的函数无非被识别,这里有一种安全和不安全的方式实现这种转化。

case1:不安全的方式

 

 
  1. class Base

  2. {

  3. public:

  4. void vritual Func()

  5. {

  6. cout<<"Base Func\n";

  7. }

  8. }

  9. class Child :public Base

  10. {

  11. public:

  12. void Func()

  13. {

  14. cout<<"Child Func\n";

  15. }

  16. void NewFunc()

  17. {

  18. cout<<"Child Newfunc\n";

  19. }

  20.  
  21. }

  22. //测试用例

 
  1. int main()

  2. {

  3. Base *b=new Child;

  4. b->Func();//输出Child Func

  5. //b->NewFunc();//错误 这个函数不是虚函数,不能多态调用

  6. Child *child=(Child *)b;//方式一不安全

  7. child->NewFunc();//Child NewFunc

  8.  
  9. Child *child=dynamic_cast<Child *>(b);//方式二安全方式

  10. if (child!=NULL)

  11. {

  12. child->NewFunc();//Child NewFunc

  13. }

  14.  
  15. return 0;

  16. }

备注:

 

1.方式一强制转换方式不安全 

不安全是因为转换的时候无法得知是否转换成功。编译器强制把b当成Child类型去使用。比如说b本来是真的指向Base而不是Child类型那么强制转换后调用Child的NewFunc可能会导致程序崩溃。

2.方式二:动态转换方式

dynamic_cast是在运行时去做转换而非编译时,所以它可以给出是否转换成功的信息。如果转换不成功则返回NULL。所以可以判断转换结果是否为NULL来决定是否能使用该指针不会导致程序崩溃

转换情况二:

父类不存在虚函数:

 

 
  1. #include <iostream>

  2. using namespace std;

  3. typedef void(*Fun)(void);

  4. class Base

  5. {

  6. public:

  7. Base(int i):a(i){b = 3;}

  8. void virtual fun() {cout << "Base's funciton" <<endl;}

  9. void this_fun(Base *b)

  10. {

  11. if (b == this)

  12. cout << "it is Base's this" <<endl;

  13. }

  14. private:

  15. int b;

  16. int a;

  17.  
  18. };

  19.  
  20. class Derived: public Base

  21. {

  22. public:

  23. Derived():Base(2),d(0){}

  24. void fun() { cout << "Derived's function" <<endl;}

  25. void this_fun(Derived *d)

  26. {

  27. if (d == this)

  28. cout << "it is Derived's this" <<endl;

  29. }

  30.  
  31.  
  32. private:

  33. int d;

  34. };

 

 

 
  1. int main()

  2. {

  3. Base b(1);

  4. Derived *d=(Derived*) &b;

  5. (*d).this_fun(d);//输出: it is Derived‘s 证明:d指针仍然为指向子类

  6. cout<<*((int *)d)<<endl;//输出:Base 的成员b的值 3 证明:d指向的仍然为父类对象

  7. cout<<*((int *)d+1)<<endl;// 输出:Base 的成员a 的值 1 证明:d指向的仍然为父类对象

  8. d->fun();//d 为指向子类的指针

  9. system("pause");

  10. return 0;

  11. }

注意:1. *((int *)d) 是将对象指针强转,访问第一个成员 ((int *)b+1)访问对象的第二个成员变量


 

 

 
  1. int main()

  2. {

  3. Base b(1);

  4. Derived *d=(Derived*) &b;

  5. (*d).this_fun(d);//输出的:it is Derived's this 证明:this指针仍然为指向子类

  6. Fun f1=(Fun)*(int*)*(int *)d; //定义一个函数指针 值为d的虚函数

  7. f1();//输出:Base’s function 证明:f1指向父类虚函数表的第一个函数

  8. cout<<*((int *)d)<<endl;//输出:一个一个地址值 17384060

  9. cout<<*((int *)d+1)<<endl;// 输出:Base 的成员b 的值 3 证明:d所指向的对象仍然为父类对象

  10. cout<<*((int *)d+2) <<endl; //输出:Base的成员a 的值 1 证明:d所指向的对象仍然为父类对象

  11. d->fun();//fun是虚函数,因此寻找虚函数表的第一个

  12. system("pause");

  13. return 0;

  14. }

分析:

1.Fun f1=(Fun)*(int*)*(int *)d;  

 从右往左分析 (int *)d 转换为一个int 类型的指针,*(int *)d,获取指针所指的内容,其实存放的是地址, 即虚函数表地址(表第一项地址) 

(int *)*(int *)d 地址强转一下,*(int *)*(int *)d,访问虚函数表的第一项,其实存放的也是指针,(Fun)强转为函数指针 ;

2.存在虚函数,此时对象布局发生改变


C++中,将父类指针转换子类指针是常见的操作,这在多态面向对象编程中是非常重要的。通过使用基指针来访问派生对象,可以实现代码的灵活性可重用性。 要将父类指针转换子类指针,您需要确保子类对象被正确地实例化,并且使用基指针指向它。然后,您可以使用强制转换来将父类指针转换子类指针。 以下是一个示例代码,展示了如何将父类指针转换子类指针: ```cpp #include <iostream> // 父类 class Parent { public: virtual void display() { std::cout << "Parent class" << std::endl; } }; // 子类 class Child : public Parent { public: void display() override { std::cout << "Child class" << std::endl; } }; int main() { // 父类指针指向子类对象 Parent* p = new Child(); // 将父类指针转换子类指针(强制转换) Child* c = static_cast<Child*>(p); // 调用子类方法 c->display(); // 输出:Child class // 清理内存 delete p; return 0; } ``` 在上面的示例中,我们创建了一个父类`Parent`的指针`p`,它指向一个派生`Child`的对象。然后,我们使用`static_cast<Child*>(p)`将父类指针转换子类指针,并将结果赋值给子类指针`c`。通过使用`c->display()`调用子类方法,我们可以确认转换成功。最后,我们使用`delete p`来释放内存。 请注意,在使用强制转换时,必须确保转换是安全的,并且派生对象确实存在于父类指针所指向的位置。否则,可能会导致未定义的行为或错误。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值