C++中的多重继承(二)
1,本文分析另一个多重继承问题及其工程中的解决方案,单继承加多接口实现的开发方式;
2,多重继承的问题三:
1,多重继承可能产生多个虚函数表:
1,实际工程中可能造成不可思议的问题,并且这些问题很难以查找和排除;
3,多重继承问题三编程实验:
#include <iostream>
#include <string> using namespace std; class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
}; class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
}; class Derived : public BaseA, public BaseB
{ }; int main()
{
Derived d;
BaseA* pa = &d;
BaseB* pb = &d;
BaseB* pbe = (BaseB*)pa; // 忘记这种转换方式
BaseB* pbc = dynamic_cast<BaseB*>(pa); // 用这种转换方式 cout << "sizeof(d) = " << sizeof(d) << endl; // 这是虚函数表指针定义的; cout << "Using pa to call funcA()..." << endl; pa->funcA(); // BaseA::funcA() cout << "Using pb to call funcB()..." << endl; pb->funcB(); // BaseB::funcB() cout << "Using pbe to call funcB()..." << endl; pbe->funcB(); // BaseB::funcA() pa 和 pb 都指向同一个对象,但是保存的值不一样,具体见 4 中分析; cout << "Using pbc to call funcB()..." << endl; pbc->funcB(); // BaseB::funcb() // 使用了 dynamic_cast,编译器就会检查 pa 指向的指针是 d 对象,进而检查d 对象有 BaseA 和 BaseB,进而认为这里转换合法,并且对指针有个修正,使得pbb 指向 pb 的位置,则调用正确; cout << endl; cout << "pa = " << pa << endl; // 0xbfd11238
cout << "pb = " << pb << endl; // 0xbfd1123c
cout << "pbe = " << pbe << endl; // 0xbfd11238 没有修正
cout << "pbc = " << pbc << endl; // 0xbfd1123c;做了修正 return ;
}
1,如果说碰上需要强制类型转换的场合,并且需要强制类型转换的是类,然后类里面又定义了虚函数,然后就是推荐使用 dynamic_cast;
4,需要进行强制类型转换时,C++ 中推荐使用新式类型转换关键字;
1,解决方案:dynamic_cast;
2,示意图:
1,pa 和 pb 都指向同一个对象,但是保存的值不一样;
2,pbb 指针指向了 pa 的虚函数表指针,所以调用了 pa 中的函数 funcB();
3,在与继承、虚函数相关的强制类型转换时,用 danamic_cast;
5,工程开发中的“多重继承”方式:
1,单继承某个类 + 实现(多个)接口;
2,示意图:
1,这是面向对象理论中所推荐的方式,实际工程开发过程中,只使用单继承;
2,单继承不可能描述生活中所有的情形,因此可以使用单继承+多接口;
3,Derived 直接继承 Base 类,然后自身实现多个接口;
6,正确的多继承方式编程实验:
#include <iostream>
#include <string> using namespace std; class Base
{
protected:
int mi; public:
Base(int i)
{
mi = i;
} int getI()
{
return mi;
} /* 在顶层父类中,定义函数判断参数指针指向的是不是当前对象(用法见 main() 函数),解决多重继承的问题一 */
bool equal(Base* obj) // 传递的指针要加上 dynamic_cast 强制类型转换;
{
return (this == obj);
}
}; /* 加减接口 */
class Interface1
{
public:
virtual void add(int i) = ;
virtual void minus(int i) = ;
}; /* 乘除接口 */
class Interface2
{
public:
virtual void multiply(int i) = ;
virtual void divide(int i) = ;
}; /* 单继承、实现多个接口 */
class Derived : public Base, public Interface1, public Interface2 // 表象上依然是 C++ 中的多继承,但是整个程序的语义上来看是单继承加上实现多个接口;
{
public:
Derived(int i) : Base(i)
{
} void add(int i) // 实现接口中的加法
{
mi += i;
} void minus(int i) // 实现接口中的加法
{
mi -= i;
} void multiply(int i) // 实现接口中的加法
{
mi *= i;
} void divide(int i) // 实现接口中的加法
{
if( i != )
{
mi /= i;
}
}
}; int main()
{
Derived d();
Derived* p = &d;
Interface1* pInt1 = &d;
Interface2* pInt2 = &d; cout << "p->getI() = " << p->getI() << endl; // pInt1->add();
pInt2->divide();
pInt1->minus();
pInt2->multiply(); cout << "p->getI() = " << p->getI() << endl; // cout << endl; cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl; // 打印 1,不转换则报错,没有这样的函数可以调用
cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl; // 打印 1,不转换则报错,没有这样的函数可以调用 return ;
}
7,正确的使用多重继承:
1,一些有用的工程建议:
1,先继承自一个父类,然后实现多个接口;
1,接口不是一个完整类实现,仅仅定义了公有函数原型而已;
2,父类中提供 equal() 成员函数;
3,equal() 成员函数用于判断指针是否指向当前对象;
4,与多重继承相关的强制类型转换用 dynamic_cast 完成;
8,小结:
1,多继承中可能出现多个虚函数表指针;
2,与多重继承相关的强制类型转换用 dynamic_cast 完成;
1,对指针的值进行一定的修正;
3,工程开发中采用“单继承多接口”的方式使用多继承;
1,仅仅在表象上是多继承,面向对象语义上不是多继承,因为仅仅继承了一个类,其它的类都是接口;
4,父类提供成员函数用于判断指针是否指向当前对象;
C++中的多重继承(二)的更多相关文章
- C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)
C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...
- C语言中如何将二维数组作为函数的参数传递
今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不 ...
- IT公司100题-35- 求一个矩阵中最大的二维矩阵(元素和最大)
问题描述: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 中最大的是: 4 5 9 10 分析: 2*2子数组的最大和.遍历求和,时 ...
- C++中的异常处理(二)
C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报 分类: C++编程语言(24) 版权声明:本文为博主原创文章,未经 ...
- c#中的linq二
c#中的linq二 using System; using System.Collections.Generic; using System.Linq; using System.Text; us ...
- [转]Visual Studio 2008中如何比较二个数据库的架构【Schema】和数据【Data】并同步
使用场景: 在团队开发中,每一个人都有可能随时更新数据库,这时候数据库中数据和架构等信息都会发生变化.如果更新不及时,就会发生数据错误或数据丢失的风险,影响团队的开发效率和 项目进度,这时候我们该怎么 ...
- php中向前台js中传送一个二维数组
在php中向前台js中传送一个二维数组,并在前台js接收获取其中值的全过程方法: (1),方法说明:现在后台将数组发送到前台 echo json_encode($result); 然后再在js页面中的 ...
- Golang中的坑二
Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...
- 以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组
学C语言,一定绕不过指针这一大难关,而指针最让人头疼的就是各种指向关系,一阶的指针还比较容易掌握,但一旦阶数一高,就很容易理不清楚其中的指向关系,现在我将通过杨辉三角为例,我会用四种方法从内存的角度简 ...
随机推荐
- Android应用程序开发中碰到的错误和获得的小经验
1,Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE Description:这表示手机内存不足,对内存较小的手机经常会出现这样的问题,从 ...
- JVM(1)之 JAVA栈
开发十年,就只剩下这套架构体系了! >>> 若想使自己编写的Java程序高效运行,以及进行正确.高效的异常诊断,JVM是不得不谈的一个话题.本"JVM进阶"专 ...
- 靶场练习--sqli(1&2)
前言 懒猪赵肥肥耍了3天3夜,每天除了练英语口语,啥子都没干.今天开始发愤图强,嘻嘻~ 计划内容有:靶场.视频.python.PHP.java.计算机英语. 首先,每天必搞靶场必看视频必学java和英 ...
- Android】Retrofit网络请求参数注解,@Path、@Query、@QueryMap...(转)
对Retrofit已经使用了一点时间了,是时候归纳一下各种网络请求的service了. 下面分为GET.POST.DELETE还有PUT的请求,说明@Path.@Query.@QueryMap.@Bo ...
- spring data mongodb CURD
一.添加 Spring Data MongoDB 的MongoTemplate提供了两种存储文档方式,分别是save和insert方法,这两种的区别: (1)save :我们在新增文档时,如果有一 ...
- HttpClient测试框架
HttpClient是模拟Http协议客户端请求的一种技术,可以发送Get/Post等请求. 所以在学习HttpClient测试框架之前,先来看一下Http协议请求,主要看请求头信息. 如何查看HTT ...
- BZOJ3207 花神的嘲讽计划I
Time Limit: 10 Sec Memory Limit: 128 MB Summary 给你一个模式串P,q个询问,对每个询问回答从Pl到Pr是否存在与给定串相同的子串,同时有所有的给定串长度 ...
- hive中Sort By,Order By,Cluster By,Distribute By,Group By的区别
order by: hive中的order by 和传统sql中的order by 一样,对数据做全局排序,加上排序,会新启动一个job进行排序,会把所有数据放到同一个reduce中进行处理,不管数 ...
- eclipse 启动项目 报错 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderLis(亲测)
[原因] 重新 clean 和 install 了maven项目后就启动报错了.解决如下: 右键项目: 属性properties 删除掉引用的其他jar 选择 Deployment Assembl ...
- 英语单词retrieve
retrieve 来源——报错信息 [root@centos65 ~]# yum whatprovides */lsb_release Loaded plugins: fastestmirror, s ...