簡體   English   中英

c + +多態性:segfault和引用數組的奇怪行為

[英]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* ,轉換的結果是一個指向ChildBase子對象的指針。 因此, (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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM