一個工作了5年的粉絲私信我。 他說自己準備了半年時間,想如螞蟻金服,結果第一面就掛了,非常難過。 問題是: “Redis存線上程安全問題嗎?” 關於這個問題,看看普通人和高手的回答。 普通人: 嗯。。。。。。。。。。。。 高手: 好的,關於這個問題,我從兩個方面來回答。 第一個,從Redis 服務端 ...
理解虛基類和虛繼承
多重繼承:代碼復用,一個派生類有多個基類。如: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()*/
C++的四種類型轉換
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()*/