2015-08-11 2 views
1

И снова плохо сформулированный вопрос, но я не знаю, как вкратце объяснить эту ситуацию:C++ Как получить указатель на текущий объект в другом классе?

У меня есть два класса. Назовем их A и B. A имеет много переменных-членов и методов. B - это структура, которая имеет shared_pointer для объекта типа A. Теперь A имеет метод, который возвращает экземпляр B (с указателем на текущий экземпляр A).

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

class C { 
public: 
    virtual B mymethod() const =0; 
    virtual int getMyvar() const =0; 
}; 

class A : public C { 
public: 
    B mymethod() const override; 
    int getMyvar() const override; //outputs myvar 
private: 
    int myvar; 
}; 

struct B { 
    std::shared_ptr<C> pointer; 
}; 

B A::mymethod() const { 
    B instance; 
    instance.pointer = std::make_shared<A>(*this); //is this wrong? 
    return instance; 
} 

Мой компилятор (GCC 4.8.2) создает исполняемые файлы для следующего кода, но во время выполнения я получаю «ошибка сегментации (ядро сбрасывали)»:

void AnotherClass::something() const { 
    A object; 
    B instance = object.mymethod(); 
    std::cout << instance.pointer->getMyvar(); //dumps the core womehow? 
} 

Я читал о std::enable_shared_from_this, но я не мог понять, как это работает или помогает мне.

Почему я могу получить сообщение об ошибке и как это исправить?

+0

объект.mymethod(); // у вас отсутствует вызов функции – tp1

+0

да, hah ... = D Нет, это простой пример. Мой сценарий слишком сложный для публикации, но этот пример объясняет это хорошо. Я не могу добраться до исходного объекта в моем случае ... = ( – Byomeer

+0

ОК, извините, отредактирован XD Просто забыли, но ошибка все еще там = ( – Byomeer

ответ

2

Из того, что я прочитал in the manual, вы делаете:

class A : public C, std::enable_shared_from_this<A> { 
public: 
    B mymethod() override; // Notice the lack of const 
private: 
    int myvar; 
}; 

, а затем:

B A::mymethod() { 
    B instance; 
    instance.pointer = shared_from_this(); // this should be right 
    return instance; 
} 

Подобно этому, все экземпляры std::shared_ptr к тому же объект разделит ту же ссылку счетчик, и он будет уничтожен только тогда, когда он должен быть.

EDIT:

Кроме того, обратите внимание, что ваш объект А должен управляться каким-либо другим std::shared_ptr перед вызовом A::mymethod(). То есть Вы должны создать объекты, как это:

std::shared_ptr<A> a_obj(new A); 

, то вы можете позвонить A::mymethod():

B b_obj = a_obj->mymethod(); 

EDIT2:

Метод A::mymethod() (и, следовательно, C::mymethod()) не может быть const, чтобы быть в состоянии для вызова метода не constshared_from_this().

+0

Компилятор говорит мне, что «std :: enable_shared_from_this - недоступная база A», когда я компилирую это ...? o.O – Byomeer

+0

Вместо 'obj (новый A);' я использовал 'std :: make_shared (...)'. Это проблема? – Byomeer

+0

К сожалению. Метод 'A :: mymethod()' не может быть 'const', потому что' shared_from_this() 'также не является' const'. Это имеет смысл (может быть?), Потому что 'shared_from_this()' необходимо изменить внутренний проверочный счетчик. – lvella

0

Мне кажется, что вы получите ошибку памяти, когда ваши объекты выйдут из сферы действия. Когда вы создаете свой объект A, а затем создаете свой shared_ptr с помощью этого указателя, ваш счетчик будет 1 вместо 2 (как вы могли бы ожидать). Таким образом, A будет уничтожен дважды, а не один раз. Более правильным способом сделать это будет создание класса A как shared_ptr и инициализация указателя в классе B с ним (не внутри класса A). Таким образом, счетчик будет увеличиваться, как вы ожидаете, и A будет удален только один раз.

+0

Фактически, ошибка времени выполнения возникает, когда shared_ptr пытается удалить локальный объект (т. Е. Он не был создан с новым). Доказательство концепции [здесь] (https://ideone.com/bGwWk3). – Christophe

+0

Вероятно, это зависит от того, как ваши объекты выходят из сферы действия. Если B выходит за пределы области до A, то A приведет к повреждению стека. – fbastian

0

Класс, который вы хотите разделить, должен унаследовать от enable_share_from_this. Затем вы можете вызвать share_from_this, чтобы получить общий указатель на класс, в котором вы находитесь.

Если вы просто попробуете make_shared, вы просто создадите отдельную группу собственности, которая будет означать две вещи, которые будут пытаться удалить ваш объект. Это станет причиной вашего segfault.

1

Предварительная проблема: как сделать снимок для доступа к myvar?

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

Во-первых, утверждение, что приводит к тому, дамп не может скомпилировать, как вы дали:

std::cout << instance.pointer->myvar; 

instance.pointer, потому что это shared_ptr<C> и C не имеет ни одного члена myvar.

Если уклонение должным образом с dynamic_pointer_cast<A>(instance.pointer)->myvar (предположим, что AnotherClass - друг), он работает.

Ваш общий указатель сделал клон: это ваши намерения?

Это утверждение:

instance.pointer = std::make_shared<A>(*this); //is this wrong? PERHAP'S !? 

создает объект клон, полученный путем построения копии из *this. Таким образом, вы не ссылаетесь на исходный объект A и, следовательно, вам не нужно std::enable_shared_from_this: количество использования instance.pointer будет равно 1, так как в данный момент есть только одна ссылка на вновь созданный общий объект.

Live demo

Или вы хотите, чтобы ссылаться на исходный объект?

Затем вы должны изменить заявление:

instance.pointer = std::shared_ptr<A>(this); //better ? 

Но это не будет компилироваться, так как mymethod() является сопзЬ, поэтому рассмотрит this как указатель на константный. Чтобы скомпилировать инструкцию, вы должны либо удалить константу mymethod(), либо добавить константу в указатель B.

Тогда это работает. Общий указатель B по-прежнему используется в количестве 1, что опять-таки нормально. Но как только этот shared_ptr выходит из области видимости, счетчик использования равен 0, а деструктор shared_ptr попытается удалить объект. КАК ЭТО БЫЛО ИНИЦИАЛИРОВАНО ЛОКАЛЬНЫЙ ОБЪЕКТ (ON STACK), это вызывает ошибку времени выполнения.

Заключительный подход

Как вы хотите иметь общие указатели на ваш объект, код AnotherClass должно быть что-то вроде:

shared_ptr<C> pobject(new A); // create the object from the free store 
B instance = pobject->mymethod(); 
... 

А класс C должен наследоваться следующим образом:

class C : public std::enable_shared_from_this<C> 
{...} 

И мой класс метода должен инициализировать shared_pointer, который он выполняет следующим образом:

//instance.pointer = std::shared_ptr<A>(this); // no, don't do no longer !! 
instance.pointer = shared_from_this(); //<===== RETURN A POINTER TO SELF 

Тогда все работает отлично.

Live demo