c ++多态性:段错误和引用数组的奇怪行为
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
class 被赋予 Other
的参考文献是
连贯,代码运行正常。
但是当 Child
class 被赋予 Other
时,第一个引用是相同的
但第二个略有不同。这样做的直接后果是
b[1].rdn.get()
总是(非常接近)'0'。另一个间接后果
是代码以 SegFault 结尾...
此外,当 Child::var
不存在时,程序运行正常,所有引用
是连贯的,没有 SegFault。
我做错了什么? child 是否无法创建 Other
Base
?如果是这样,看起来多态性被破坏了......
编辑
根据一个不错的回答:
#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 {...}
这是一个 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
class 被赋予 Other
的参考文献是
连贯,代码运行正常。
但是当 Child
class 被赋予 Other
时,第一个引用是相同的
但第二个略有不同。这样做的直接后果是
b[1].rdn.get()
总是(非常接近)'0'。另一个间接后果
是代码以 SegFault 结尾...
此外,当 Child::var
不存在时,程序运行正常,所有引用
是连贯的,没有 SegFault。
我做错了什么? child 是否无法创建 Other
Base
?如果是这样,看起来多态性被破坏了......
编辑
根据一个不错的回答:
#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 {...}