多重继承:代码复用,一个派生类有多个基类。如:class C: public A,public B{};
虚基类:virtual可以修饰继承方式,是虚继承,被虚继承的类,称作虚基类。class A:virtual public B{};
虚继承的类中会多一个vbptr指向vbtable,Vbtable中保存的是虚基类中数据在派生类中的内存偏移量,从虚基类中继承的成员变量会被放在派生类内存的最下端。

虚函数和虚基类在调用的时候是没有问题的,

但是在delete的时候会发生堆报错

原因是:基类指针类型的成员p指向派生类对象,永远指向的是派生类基类部分数据的起始地址,这里的基类A的起始位置就是vfptr。但是这里的派生类B是虚继承的A,虚继承的部分会放到派生类内存的后面,p指向的就是派生类后面的内存,这种情况Delete p就不会删除派生类中的内存,造成了上图中的问题。

class A{
public:
virtual void func(){cout<<"call A:func"<<endl;}
void operator delete (void* ptr){
cout<<"operator delete:"<<ptr<<endl;
free(ptr);
}
private:
int ma;
};
class B:virtual public A{
public:
void func(){cout<<"call B:func"<<endl;}
void* operator new (size_t size){
void *p= malloc(size);
cout<<"operator new:"<<p<<endl;
return p;
}
private:
int mb;
};
int main(){
A *p=new B();
cout<<"main:p"<<p<<endl;
p->func();
delete p;
}

delete的内存地址与new的内存地址不同,所以会造成问题。

继承的样子像菱形,叫菱形继承。类D中会继承两个类A中的成员。尽量避开多重继承。
多重继承的好处:可以做更多代码的复用。
用虚继承解决上面的问题。
class A{
public:
A(int data):ma(data){
cout<<"A()"<<endl;
}
~A(){
cout<<"~A()"<<endl;
}
protected:
int ma;
};
class B:public A{
public:
B(int data):A(data),mb(data){
cout<<"B()"<<endl;
}
~B(){
cout<<"~B()"<<endl;
}
protected:
int mb;
};
class C:public A{
public:
C(int data):A(data),mc(data){
cout<<"C()"<<endl;
}
~C(){
cout<<"~C()"<<endl;
}
protected:
int mc;
};
class D:public B,public C{
public:
D(int data):B(data),C(data),md(data){
cout<<"D()"<<endl;
}
~D(){
cout<<"~D()"<<endl;
}
protected:
int md;
};
int main(){
D d(10);
return 0;
}
/*
输出
A()
B()
A()
C()
D()
~D()
~C()
~A()
~B()
~A()*/
如果虚继承就会在B和C中构造出vbptr指针,在D中指向类A中的成员变量。

使用虚继承避免继承多次的问题:
class B:virtual public A{//使用虚继承避免菱形继承的问题
public:
B(int data):A(data),mb(data){
cout<<"B()"<<endl;
}
~B(){
cout<<"~B()"<<endl;
}
protected:
int mb;
};
class C:virtual public A{
public:
C(int data):A(data),mc(data){
cout<<"C()"<<endl;
}
~C(){
cout<<"~C()"<<endl;
}
protected:
int mc;
};
class D:public B,public C{
public:
D(int data):A(data),B(data),C(data),md(data){
cout<<"D()"<<endl;
}
~D(){
cout<<"~D()"<<endl;
}
protected:
int md;
};
/*
输出结果:
A()
B()
C()
D()
~D()
~C()
~B()
~A()*/
const_cast:去掉(指针或者引用)常量属性的一个类型转换
static_cast:提供编译器认为安全的类型转换 基类和派生类可以通过static_cast进行转换
reinterpret_cast:类似于c风格的强制类型转换(不安全)
dynamic_cast:主要用在继承结构中,可以支持RTTI类型识别的上下转换
解释一下dynamic_cast的用法:
class Base{
public:
virtual void func()=0;
};
class Derive1:public Base{
public:
void func() override {
cout<<"Derive1::func()"<<endl;
}
};
class Derive2:public Base{
public:
void func() override {
cout<<"Derive2::func()"<<endl;
}
//如果想要在这个类里添加一个新业务
void funcDerive2(){
cout<<"Derive2::funcDerive2()"<<endl;
}
};
/**
* 外部调用上面两个类的接口
* @param p
*/
void show(Base* p){
//dynamic_cast会检查p指针是否指向的是一个Derive2类型的对象
//如果是,dynamic_cast转换类型成功,返回Derive2对象的地址给dp2;否则返回nullptr
Derive2 *dp2=dynamic_cast<Derive2*> (p);
if(dp2!= nullptr){
dp2->funcDerive2();
}else
p->func();
}
int main(){
Derive1 d1;
Derive2 d2;
show(&d1);
show(&d2);
return 0;
}
/*
输出结果:
Derive1::func()
Derive2::funcDerive2()*/
所以我开始关注ruby,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出
给定以下方法:defsome_method:valueend以下语句按我的预期工作:some_method||:other#=>:valuex=some_method||:other#=>:value但是下面语句的行为让我感到困惑:some_method=some_method||:other#=>:other它按预期创建了一个名为some_method的局部变量,随后对some_method的调用返回该局部变量的值。但为什么它分配:other而不是:value呢?我知道这可能不是一件明智的事情,并且可以看出它可能有多么模棱两可,但我认为应该在考虑作业之前评估作业的右侧...我已经在R
我在我的Rails3示例应用程序上使用CarrierWave。我想验证远程位置上传,因此当用户提交无效URL(空白或非图像)时,我不会收到标准错误异常:CarrierWave::DownloadErrorinImageController#createtryingtodownloadafilewhichisnotservedoverHTTP这是我的模型:classPaintingtrue,:length=>{:minimum=>5,:maximum=>100}validates:image,:presence=>trueend这是我的Controller:classPaintingsC
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型
我正在研究使用EventMachine支持的twitter-streamrubygem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@
啊,正则表达式有点困惑。我正在尝试删除字符串末尾所有可能的标点符号:ifstr[str.length-1]=='?'||str[str.length-1]=='.'||str[str.length-1]=='!'orstr[str.length-1]==','||str[str.length-1]==';'str.chomp!end我相信有更好的方法来做到这一点。有什么指点吗? 最佳答案 str.sub!(/[?.!,;]?$/,'')[?.!,;]-字符类。匹配这5个字符中的任何一个(注意,。在字符类中并不特殊)?-前一个字符或组
我使用的是遗留数据库,所以我无法控制数据模型。他们使用了很多多态链接/连接表,就像这样createtableperson(per_ident,name,...)createtableperson_links(per_ident,obj_name,obj_r_ident)createtablereport(rep_ident,name,...)其中obj_name是表名,obj_r_ident是标识符。因此链接的报告将按如下方式插入:insertintoperson(1,...)insertintoreport(1,...)insertintoreport(2,...)insertint
所以我只是对此感到好奇:DataMapper为其模型使用混合classPostincludeDataMapper::Resource虽然active-record使用继承classPost有谁知道为什么DataMapper选择这样做(或者为什么AR选择不这样做)? 最佳答案 它允许您从另一个不是DM类的类继承。它还允许动态地将DM功能添加到类中。这是我正在处理的模块中的类方法:defdatamapper_classklass=self.dupklass.send(:include,DataMapper::Resource)klass
在Ruby中有运算符(operator)。在API中,他们没有命名它的名字,只是:Theclassmustdefinetheoperator...Comparableusestoimplementtheconventionalcomparison......theobjectsinthecollectionmustalsoimplementameaningfuloperator...它叫什么名字? 最佳答案 参见上面的@Tony。然而,它也被称为(俚语)“宇宙飞船运算符(operator)”。