[英]c++ Polymorphism : segfault and reference strange behaviour with arrays
這是一個MWE ,當使用g++ -std=c++11
編譯時,會產生分段錯誤:
#include <iostream>
#include <random>
class Rand{
public:
Rand(double const& min_inclusive, double const& max_exclusive):
mt_(std::random_device()()),
dist_(min_inclusive,max_exclusive){}
~Rand(){}
double get() { return dist_(mt_); }
private:
std::mt19937_64 mt_;
std::uniform_real_distribution<double> dist_;
};
class Base {
public:
Base():rnd(0.0,1.0){ std::cout<<"Base created "<<&rnd<<" "<<rnd.get()<<std::endl; }
virtual ~Base(){};
Rand rnd;
};
class Child: public Base{
public:
Child():var(1.0){ std::cout<<"Child created"<<std::endl; }
double var;
};
class Other {
public:
Other(Base* b):b(b){ std::cout<<"Other created "<<&(b[0].rnd)<<" and "<<&(b[1].rnd)<<"--->"<<b[0].rnd.get()<<" "<<b[1].rnd.get()<<std::endl; }
Base* b;
};
int main(){
unsigned int N(2);
std::cout<<"#############"<<std::endl;
Base* b(new Base[N]);
Other o1(b);
std::cout<<"#############"<<std::endl;
Child* c(new Child[N]);
Other o2(c);
std::cout<<"#############"<<std::endl;
delete[] b;
delete[] c;
}
典型的輸出是:
#############
Base created 0x239c020 0.226514
Base created 0x239ca00 0.902337
Other created 0x239c020 and 0x239ca00--->0.864321 0.302185
#############
Base created 0x239d3f0 0.573563
Child created
Base created 0x239ddd8 0.422187
Child created
Other created 0x239d3f0 and 0x239ddd0--->0.909183 4.94066e-324
#############
在將Base
類提供給Other
的第一部分中,引用是一致的,並且代碼可以正常運行。
但是,當將Child
類提供給Other
,第一個引用是相同的,但第二個引用則略有不同。 這樣做的直接結果是b[1].rdn.get()
始終(非常接近)為'0'。 另一個間接后果是代碼以SegFault結尾。
此外,如果不存在Child::var
,則程序可以正常運行,所有引用均一致,並且沒有SegFault。
我究竟做錯了什么 ? 用Base
的子Base
創建Other
是不可能的嗎? 如果是這樣,看起來多態性就毀了……
根據一個不錯的答案:
#include <iostream>
#include <random>
#include <vector>
#include <memory>
class Rand{
public:
Rand(double const& min_inclusive, double const& max_exclusive):
mt_(std::random_device()()),
dist_(min_inclusive,max_exclusive){}
~Rand(){}
double get() { return dist_(mt_); }
private:
std::mt19937_64 mt_;
std::uniform_real_distribution<double> dist_;
};
class Base {
public:
Base():rnd(0.0,1.0){ std::cout<<"Base created "<<&rnd<<" "<<rnd.get()<<std::endl; }
virtual ~Base(){};
Rand rnd;
};
class Child: public Base{
public:
Child():var(0.0){ std::cout<<"Child created"<<std::endl; }
double var;
};
template<typename Type>
class Other {
public:
Other(unsigned int N):b_(N){
std::cout<<"Other created "<<std::endl;
for(unsigned int i(0);i<b_.size();i++){
b_[i] = std::make_shared<Type>();
}
}
std::vector<std::shared_ptr<Base> > b_;
};
int main(){
unsigned int N(2);
std::cout<<"#############"<<std::endl;
Other<Base> o1(N);
std::cout<<"#############"<<std::endl;
Other<Child> o2(N);
std::cout<<"#############"<<std::endl;
}
如果您有一個Child
數組,則不能將其視為Base
數組。 這意味着例如當您調用b[1].rnd.get()
,您將調用未定義的行為。
這是為什么? 當您擁有Child* c
並將其轉換為Base*
,轉換的結果是一個指向Child
的Base
子對象的指針。 因此, (Base*)c + 1
將指向該子對象之后的下一個字節,在您的情況下,它是var
的第一個字節。
當Child
沒有數據成員時, (Base*)c + 1
指向下一個對象,因此一切正常。
為了實現多態行為,您應該創建一個指向 Base
的指針數組:
Base** b{new Base*[N]};
使用Base
任何后代初始化每個數組元素,並享受多態性。
編輯:正如vsoftco在評論中合理指出的那樣,最好不要使用原始指針和數組:
std::vector<std::shared_ptr<Base>> b {...}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.