关于 ABC、继承和虚函数的 C++ 赋值,试图找出段错误等

标签 c++ inheritance segmentation-fault virtual abstract-class

我有一个 C++ 作业,我们必须在其中实现一些继承自 ABC 的类,称为 Object。每个对象都有一个特定的 ID。

我们有一个 String 类(使用 std::string 实现),它是一个对象,将被用来代替 std::string。

我们还有一个 PlaneComponent ABC,它是一个对象并且有一个数据成员“String description”。 另一个类是 PassengerCompartment,它是一个 PlaneComponent 并且有一个数据成员,该数据成员是指向另一个 PassengerCompartment(如内部 PassengerCompartment)的指针。

我们有一个函数“克隆”,它返回一个对象*给克隆的对象。
我在 main 中创建了一个名为“test”的 PassengerCompartment 对象和一个 Object* ptr 并克隆了“test”对象。
然后我检查了这些 Objects(PassengerCompartments) 是否相等并得到“不相等”,这是不正确的。 所以我开始检查相等的功能。 问题是当我调用 String::equal 并且在它的主体中我有这个命令:

cout << ((String*)other_object)->get_string() << endl;

(get_string() 是对返回 std::string 的 Class String 的 std::string 的访问器)
我遇到段错误:

Program received signal SIGSEGV, Segmentation fault.
0xb76e2d88 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string () from /usr/lib/libstdc++.so.6

这是 gdb 中 bt full 的输出:

(gdb) bt full
#0  0xb767ed88 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string () from /usr/lib/libstdc++.so.6
No symbol table info available.
#1  0x0804bb10 in String::get_string (this=0xbfdb5560) at objects.h:55
No locals.
#2  0x0804b2de in String::equal (this=0xbfdb5568, other_object=0xbfdb5560) at     objects.cpp:129
No locals.
#3  0x0804b5a5 in PlaneComponent::equal (this=0xbfdb5560, other_object=0xbfdb5560) at objects.cpp:191
No locals.
#4  0x0804ba3a in PassengerCompartment::equal (this=0xbfdb5560, other_object=0xbfdb5560) at objects.cpp:372
result = false
#5  0x080492b4 in main () at main.cpp:47
anew = {<Object> = {_vptr.Object = 0x804c008, id = 1}, the_string = {static npos = 4294967295, 
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x804f014 "A Class"}}}
test = {<PlaneComponent> = {<Object> = {_vptr.Object = 0x804bfc8, id = 2}, description = {<Object> = {_vptr.Object = 0x804c008, id = 1}, 
  the_string = {static npos = 4294967295, 
    _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
      _M_p = 0x804f014 "A Class"}}}}, inner_PassengerCompartment = 0x0}
ptr = (class Object *) 0x804f020

这是 valgrind 的输出:

==14233==  Bad permissions for mapped region at address 0x804C004
==14233==    at 0x40CFD88: std::string::string(std::string const&) (in /usr/lib/libstdc++.so.6.0.9)
==14233==    by 0x804BB0F: String::get_string() (objects.h:55)
==14233==    by 0x804B2DD: String::equal(Object const*) (objects.cpp:129)
==14233==    by 0x804B5A4: PlaneComponent::equal(Object const*) (objects.cpp:191)
==14233==    by 0x804BA39: PassengerCompartment::equal(Object const*) (objects.cpp:372)
==14233==    by 0x80492B3: main (main.cpp:47)

在 String::equal(Object* other_object) 中,在 gdb 中我得到这个:

(gdb) print *this
$1 = {<Object> = {_vptr.Object = 0x804bfa8, id = 1}, the_string = {static npos = 4294967295, 
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x804f014 "A Class"}}}

(gdb) print *other_object 
$2 = {_vptr.Object = 0x804bf68, id = 2}

所以在我看来,Object* ptr 只是一个对象,也不是 PassengerCompartment。

所以问题出在克隆函数上? (在这个函数“Object* PassengerCompartment::clone(void)”中我有这个:

return new PassengerCompartment(*this);

我做错了什么?我真的想不通。也许复制构造函数? 请告诉我您希望我包含哪些其他信息!

预先感谢您的帮助!

一些额外的注意事项:
Object* Object::clone() 是纯虚拟的。

This is the code of the equal functions and the copy constructors
<强> These are the source files as asked ||| Mirror here

最佳答案

在检查了完整的源代码之后,它有很多问题。但是有 3 条主线实际上是错误的,其余的归结为 const 正确性。

一个。在 PassengerCompartment::equal 中,您没有返回任何东西。您需要返回结果;不返回 void 的函数中缺少返回值将调用未定义的行为。

B.

cout << dynamic_cast<const String&>(*other_object).get_string() << endl;

您正在尝试将 PlaneComponent 引用转换为 String 引用。 PlaneComponent 不会在其层次结构中的任何位置继承 String。我怀疑你的意思是:

cout << other_object->toString().get_string() << endl;

C.

if ( the_string == ((String*)other_object)->the_string)

与 B 相同的问题。PlaneComponent 未实现 String 接口(interface),因此无法转换为它。这是非法转换,会调用未定义的行为。

您的其余问题归结为常量正确性。当涉及到函数参数时,您对 const-correctness 很在行。例如,equal 方法不会修改我们正在比较的对象,因此您将其视为只读指针:很好。但是,在将方法定义为只读时,您根本没有这样做。例如:

virtual bool equal(const Object* other_object);

应该是:

virtual bool equal(const Object* other_object) const;

如果不始终如一地执行此操作,上述更正将引发编译器错误。这是更正后的完整来源:

///////////////////////////////////////////////////////////////////////////////
// objects.h
#include <iostream>
#include <string>

class String;

class Object
{
  private:
    int id;
  public:
    //constructors and destructor
    Object(void);
    Object(const Object& object_);
    virtual ~Object(void);

    //general functionality
    virtual bool equal(const Object* other_object) const { return (id == other_object->id); }
    bool identical(const Object& other_object) const { return (this == &other_object); }
    virtual String toString(void) const = 0;
    virtual Object* clone(void) const = 0;

    //accessor
    int get_id(void) const {return id;} 
};


///////////////////////////////////////////////////////////////////////////////
// objects.cpp
class String : public Object
{
  private:
    std::string the_string;
  public:
    //constructors, assignment operator and destructor
    String(void);
    String(std::string given);
    String(const String& given);
    String& operator=(const String& given);
    virtual ~String(void);
    //String(const std::string& given) 

    //general functionality
    int length(void);
    void clear(void);
    char& at(int pos);
    void updateAT(int pos, std::string given);
    void concat(const String& given);
    void print(void);

    virtual String toString(void) const;
    virtual Object* clone(void) const;
    virtual bool equal(const Object* other_object) const;


    //accessor
    std::string get_string(void) const { std::cout << the_string << std::endl; return the_string; }
};


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class PlaneComponent : public Object
{
  private:
    String description;
  public:
    //constructor and destructor
    PlaneComponent(const String& describe_it);
    PlaneComponent(const PlaneComponent& given);
    virtual ~PlaneComponent(void);

    //general functionality
    virtual void ready_check(void) = 0;
    //virtual void process(void) = 0; 
    virtual String toString(void) const = 0;
    virtual bool equal(const Object* other_object) const = 0;
    virtual Object* clone(void) const = 0;

    //accessor
    String get_description(void) const { return description; }
};


///////////////////////////////////////////////////////////////////////////////
class PassengerCompartment : public PlaneComponent
{
  private:
    PassengerCompartment* inner_PassengerCompartment;
  public:
    //constructor and destructor
    PassengerCompartment(const String& description_given);
    PassengerCompartment(const PassengerCompartment& given);
    //constructors for the inner passenger_compartment
    PassengerCompartment(const String& description_given, bool for_innerPC);
    PassengerCompartment(const PassengerCompartment& given, bool for_innerPC);
    virtual ~PassengerCompartment(void);

    //general functionality
    //void more_space(const String& inner_description);
    virtual String toString(void) const;
    virtual void ready_check(void);
    virtual Object* clone(void) const;
    virtual bool equal(const Object* other_object) const;


    //accessor
    PassengerCompartment* get_inner_PassengerCompartment(void);
};


///////////////////////////////////////////////////////////////////////////////




#include <iostream>
#include <sstream>
#include <cstdlib>
using namespace std;

///////////////////////////////////////////////////////////////////////////////
Object::Object(void)
{
    cout << "Object just created!" << endl;
    static int id_ = 1;
    id = id_;
    id_++;
}


Object::Object(const Object& object_) 
{ 
    id = object_.id; 
    cout << "Copy of Object just created!" << endl;
}


Object::~Object(void) 
{ 
    id = -1; 
    cout << "Object to be destroyed!" << endl;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
String::String(void) 
{ 
    cout << "String just created!" << endl;
}


String::String(std::string given) 
{ 
    the_string = given;
    cout << "String just created!" << endl;
}


String::String(const String& given) : Object(given) 
{
    the_string = given.the_string;
    cout << "Copy of String just created!" << endl;
}


String::~String(void) 
{ 
    the_string.empty(); 
    cout << "String to be destroyed!" << endl; 
}


String& String::operator=(const String& given)
{
    if (&given != this)
    {
        this->the_string = given.the_string;
    }
    return *this;
}

////////////////////////////////////////////
int String::length(void)
{
    return the_string.length();
}


void String::clear(void)
{
    the_string.clear();
}


char& String::at(int pos)
{
    return the_string.at(pos);
}


void String::updateAT(int pos, string given)
{
    the_string.replace(pos, 1, given);
}


void String::concat(const String& given)
{
    the_string.append(given.the_string);
}


void String::print(void)
{
    for (int i = 0; i < the_string.length(); i++)
    {
        cout << the_string.at(i);
    }
    cout << endl;
}
////////////////////////////////////////////
String String::toString(void) const
{
    ostringstream resultstream; 
    resultstream << "String with content: <<" << the_string
                 << ">> and id: " << Object::get_id();
    String result( resultstream.str() );
    return result;
}


Object* String::clone(void) const
{
    return new String(*this);
}


bool String::equal(const Object* other_object) const
{
    cout <<"the_string: " << endl;
    //this->print(); 
    cout << this->get_string() << endl;
    cout <<"other_object the_string: " << endl;
    cout << other_object->toString().get_string() << endl;
    //cout << dynamic_cast<String*>(other_object)->get_string() << endl;
    //cout << ((String*)other_object)->get_string() << endl; 
    //cout << ((String*)other_object)->the_string << endl;
    //((PassengerCompartment*)other_object)->get_description().print();
    //check if strings are the same
    if ( the_string == other_object->toString().get_string())
    {
        //check id
        if ( Object::equal(other_object) )
            return true;
        else
            return false;
    }
    else
        return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
PlaneComponent::PlaneComponent(const String& describe_it) : description(describe_it)
{ 
    cout << description.get_string() << endl;
    cout << "PlaneComponent just created!" << endl;
}


PlaneComponent::PlaneComponent(const PlaneComponent& given) : Object(given), description(given.description)
{ 
    cout << description.get_string() << endl;
    cout << "PlaneComponent just created!" << endl; 
}


PlaneComponent::~PlaneComponent(void) 
{ 
    cout << "PlaneComponent to be destroyed!" << endl; 
}


/*PlaneComponent::PlaneComponent(const String& describe_it)
{
    description = describe_it;
    cout << "PlaneComponent just created!" << endl; 
}*/


////////////////////////////////////////////
bool PlaneComponent::equal(const Object* other_object) const
{
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    cout << "--------PlaneComponent::equal----------" << endl;
    this->get_description().print();
    dynamic_cast<const PlaneComponent*>(other_object)->get_description().print();
    cout << "--------PlaneComponent::equal----------" << endl;
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    cout << "------------------" << endl;
    //check if description is the same
    //if ( description.equal(((PassengerCompartment*)other_object)->get_description) )
    if ( description.equal(other_object) )
    {
        //check id
        if ( Object::equal(other_object) )
            return true;
        else
            return false;
    }
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
PassengerCompartment::PassengerCompartment(const String& description_given) : PlaneComponent(description_given)
{
    inner_PassengerCompartment = NULL;
    cout << "PassengerCompartment just created!" << endl;
    //float random = ((float)rand())/(float)(RAND_MAX);
    //if ( random >= 0.5 )
    //{
    //  String inner_result("Inner: ");
    //  inner_result.concat(description_given);
    //  inner_PassengerCompartment = new PassengerCompartment(inner_result, true);
    //}
}


PassengerCompartment::PassengerCompartment(const PassengerCompartment& given) : PlaneComponent(given)
{
    if (given.inner_PassengerCompartment != NULL )
    {//if the given PassengerCompartment has an inner PassengerCompartment, copy that, too
        inner_PassengerCompartment = new PassengerCompartment(*(given.inner_PassengerCompartment), true);
        std::cout << "Inner PassengerCompartment just created!" << std::endl;
    }
    else
        inner_PassengerCompartment = NULL;
    std::cout << "PassengerCompartment just created!" << std::endl;
}


PassengerCompartment::PassengerCompartment(const String& description_given, bool for_innerPC) : PlaneComponent(description_given)
{
    static bool already_allocated_inner_space = false;
    if ( !already_allocated_inner_space )
    {
        inner_PassengerCompartment = NULL;
        already_allocated_inner_space = true;
    }
}


PassengerCompartment::PassengerCompartment(const PassengerCompartment& given, bool for_innerPC) : PlaneComponent(given)
{
    inner_PassengerCompartment = NULL;
}


PassengerCompartment::~PassengerCompartment(void)
{
    static bool did_it = false;
    if ( !did_it )
    {
        if ( inner_PassengerCompartment != NULL )
        {
            did_it = true;
            delete inner_PassengerCompartment;
            inner_PassengerCompartment = NULL;
            cout << "Inner Passenger Compartment to be destroyed!" << endl;
        }
        cout << "Passenger Compartment to be destroyed!" << endl;
        did_it = false;
    }
}


////////////////////////////////////////////
/*void PassengerCompartment::more_space(const String& inner_description)
{
    static bool have_added_space = false;
    if (!have_added_space)
    {
        float random = ((float)rand())/(float)(RAND_MAX);
        if ( random >= 0.5 )
        {
            inner_PassengerCompartment = new PassengerCompartment(inner_description);
        }
        have_added_space = true;
    }   
}*///////writen a copy constructor instead!!

String PassengerCompartment::toString(void) const
{
    ostringstream resultstream; 
    resultstream << "This is a Passenger Compartment with description: \"";
    resultstream << get_description().get_string(); 
    resultstream << "\" and id: " << Object::get_id();
    resultstream << endl;
    if ( inner_PassengerCompartment == NULL)
        resultstream << "With no inner Passenger Compartment";
    else
    {
        resultstream << "With an inner Passenger Compartment with description: \"";
        resultstream << inner_PassengerCompartment->get_description().get_string();
        resultstream << "\" and id:" << ((Object*)inner_PassengerCompartment)->get_id();
    }
    String result( resultstream.str() );
    return result;
}


void PassengerCompartment::ready_check(void)
{
    cout << "Description of Passenger Compartment:" << endl;
    get_description().print();  
    cout << "Passenger Compartment OK!" << endl;
    if ( inner_PassengerCompartment != NULL )
    {
        cout << "Description of Inner Passenger Compartment:" << endl;
        inner_PassengerCompartment->get_description().print();
        cout << "Inner Passenger Compartment OK!" << endl;
    }
}


Object* PassengerCompartment::clone(void) const
{
    cout << "_______________PassengerCompartment::Clone_______________" << endl;
    return new PassengerCompartment(*this);
}


////////////////////////////////////////////
PassengerCompartment* PassengerCompartment::get_inner_PassengerCompartment(void)
{
    return inner_PassengerCompartment;
}


bool PassengerCompartment::equal(const Object* other_object) const
{
    bool result = false;
    //if there is an inner compartment check if equal
    if ( inner_PassengerCompartment != NULL )
    {   /*//manually check for it's description and ID to avoid recursion
        if ( get_description() == ((PlaneComponent*)other_object)->get_description() )
        {   //check IDs
            if ( get_id() == other_object->get_id() )
                    result = true;
            else    result = false;
        }
        else result = false;*/
        if ( inner_PassengerCompartment->PlaneComponent::equal( ((PassengerCompartment*)other_object)->inner_PassengerCompartment ) )
                result = true;
        else    result = false;
    }

    this->get_description().print();
    dynamic_cast<const PassengerCompartment*>(other_object)->get_description().print();
    cout << "------------------" << endl;
    cout << "------------------" << endl;

    //check for the passenger compartment itself
    if ( PlaneComponent::equal(other_object) )
        result = true;
    else    
        result = false;
    return result;
}

///////////////////////////////////////////////////////////////////////////////
// main.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main(void)
{
    //seed rand
    srand( (unsigned int) (time(NULL)) );


    String anew("A Class");
    PassengerCompartment test(anew);


    Object* ptr = test.clone();

    cout << "THIS ID: " << test.get_id() << endl;
    cout << "OTHER OBJECT ID:" << ptr->get_id() << endl;
    if (test.equal(&test))
    {
        cout << "Equal" << endl;
    }
    else cout << "Not Equal" << endl;


    cout << "-------------------Exiting main!----------------" << endl;
    return 0;
}

最后,一定要清理你分配的资源。克隆的对象应该在某个时候被删除,但如果你使用类似 shared_ptr 的东西(来自 Boost 或 C++11)会更好。

最后一点:根据您散布在整个代码中的输出量来判断,我猜您还不知道如何使用调试器。我强烈推荐你学习这个。如果您可以学习如何使用调试器单步执行代码并查看执行之间的各种状态,那么您的许多挫败感都会消失,您不必觉得自己是在黑暗中编码。

关于关于 ABC、继承和虚函数的 C++ 赋值,试图找出段错误等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9154333/

相关文章:

c# - 如何为其派生类型的每种可能组合实现基类的方法

c++ - C++ 中的动态链接不起作用

inheritance - Jenkins groovy 管道简单继承故障

C - 通过函数、指针写入动态数组

c++ - 通过引用传递 "advanced"概念?

c++ - 使用 Integral Image 有效地求和矩阵行中的元素

c++ - 使用 SFML 的 C++ 中缺少声明的错误

C,为什么这段代码适用于数组表示法,但不适用于指针表示法?

c - 段错误(核心已转储)-找不到原因

c++ - 何时使用 std::async 与 std::threads?