C ++ назначение на ABC, наследование и виртуальные функции, пытаясь выяснить segfault и многое другое - PullRequest
1 голос
/ 06 февраля 2012

У меня есть присвоение C ++, в котором мы должны реализовать некоторые классы, которые наследуются от ABC с именем Object. Каждый объект имеет определенный идентификатор.

У нас есть класс String (для его реализации используется std :: string), который является объектом Object и будет использоваться вместо std :: string.

Также у нас есть ABC PlaneComponent, который является Объектом и имеет дата-столбец «Описание строки». Другим классом является PassengerCompartment, который является PlaneComponent и имеет элемент данных, который является указателем на другой PassengerCompartment (например, внутренний PassengerCompartment).

У нас есть функция "клон", которая возвращает объект * клонированному объекту.
Я создаю объект PassengerCompartment под названием «test» в main и Object * ptr и клонирую объект «test».
Затем я проверил, равны ли эти объекты (PassengerCompartments) и получил «не равный», что не правильно. Поэтому я начал проверять равные функции. Проблема в том, что когда я вызываю String :: equal и в его теле я получаю следующую команду:

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

(get_string () - это метод доступа к std :: string класса 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

Это вывод bt full в gdb:

(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 () является чисто виртуальным.

Это код равных функций и конструкторов копирования
Это исходные файлы в соответствии с запросом ||| Зеркало здесь

Ответы [ 3 ]

2 голосов
/ 06 февраля 2012

После изучения полного исходного кода у него много проблем.Но есть три основные строки, которые на самом деле ошибочны, а остальные сводятся к константности.

A.В PassengerCompartment :: равных вы ничего не возвращаете.Вам нужно вернуть результат; Отсутствие возвращаемого значения в функции, которая не возвращает 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 и, следовательно, не может быть приведена к нему.Это недопустимое приведение и вызовет неопределенное поведение.

Остальные проблемы сводятся к константности.Вы хорошо относитесь к константности, когда дело доходит до параметров функции.Например, метод 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).

Последнее замечание: судя по количеству вывода, которое вы растянули по всему кодуЯ полагаю, вы еще не знаете, как использовать отладчик.Я не могу рекомендовать достаточно сильно, чтобы вы узнали это.Многие ваши разочарования могут уйти, и вам не нужно чувствовать, что вы программируете в темноте, если вы можете научиться обходить код с помощью отладчика и просматривать различные состояния между исполнениями.

1 голос
/ 06 февраля 2012

Если вы замените

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

с этим:

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

тогда программа завершится с исключением, которое должно указать вам, где настоящая проблема (возможно, вы приводите объект, который не является специализацией String)

Примечание: String :: get_string () должно быть const

class String
{
public:
  // ....
  std::string get_string() const { /*...*/ };
};

Примечание 2:

вы не возвращаетесь из PlaneComponent :: равно в случае, когда описания равны

//check if description is the same
if ( description.equal(other_object) )
{
  //check id
  if ( Object::equal(other_object) )
    return true;
  else
    return false;
}
return false; // <<< this is missing
1 голос
/ 06 февраля 2012
(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}

Из vptrs вы можете видеть, что другой объект не является String, поэтому приведение в этой строке

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

вероятно незаконно.

Может быть, конструкторы копирования?

Может быть. Может быть, это может быть equals метод.

  • отправьте больше кода, особенно метод equals (для всех классов), и, возможно, также скопируйте ctor
  • проверить, к какому типу относится другой объект. print /a 0x804bf68 должен дать вам подсказку

Edit:

Из кода, который вы разместили, я думаю, что эта строка

    if ( description.equal(other_object) )

в PlaneComponent::equal должно быть

    if ( description.equal(((PassengerCompartment*)other_object)->get_description()) )

Обратите внимание, что вы должны проверить во всех ваших equals методах, что другой объект имеет соответствующий тип (чтобы не удивляться, когда это не так).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...