C++: доступ к родительскому классу из дочернего класса

У меня есть два класса:

class ClassA {
    public:
        ClassB *classB;
        int i = 100;
}
// and:
class ClassB {
    public:
        void longProcess();
}

Я запускаю пустоту из ClassB():

ClassA classA = new ClassA();
classA->i = 100;
classA->classB = new ClassB();
classB->longProcess(); // it's a long process!
// but when it will finish - I need to get the "i" variable from ClassA

Как получить переменную «int i» из метода: longProcess()? На самом деле мне нужно запустить этот длинный код в другом потоке, поэтому мне нужно получить переменную «i» из ClassB, когда longProcess() завершит свою работу. Какие-либо предложения?

Обновление: я пытаюсь написать код для сохранения указателя на родительский класс

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ ChildClass.h ]-=-=-=- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include "ParentClass.h"
class ChildClass {
    public:
        ChildClass();
        ParentClass *pointerToParentClass; // ERROR: ISO C++ forbids declaration of 'ParentClass' with no type
        void tryGet_I_FromParentClass();
};

ОШИБКА: ISO C++ запрещает объявление «ParentClass» без типа

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ ChildClass.cpp ]-=-=-=- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include "ChildClass.h"
ChildClass::ChildClass(){}
void ChildClass::tryGet_I_FromParentClass(){
    // this->pointerToParentClass...??? it's not work
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ ParentClass.h ]-=-=-=- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include "ChildClass.h"
class ParentClass {
    public:
        ParentClass();
        ChildClass *childClass;
        int i;
};

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ ParentClass.cpp ]-=-=-=- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include "ParentClass.h"
ParentClass::ParentClass(){
    childClass = new ChildClass();
    childClass->pointerToParentClass = this;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ MainWindow.cpp ]-=-=-=- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=

ParentClass *parentClass = new ParentClass();

person JavaRunner    schedule 10.10.2012    source источник
comment
Не давайте переменной то же имя, что и класс, за исключением одной заглавной буквы. Это заставит людей хотеть дать вам пощечину. Такие люди, как я.   -  person Beta    schedule 10.10.2012
comment
@slashmais, извините, это ошибка. На самом деле это один и тот же класс. Я исправил это.   -  person JavaRunner    schedule 10.10.2012
comment
Кажется, вы объявляете ParentClass как в ChildClass.h, так и в ParentClass.h. Это предназначено?   -  person Simon Germain    schedule 10.10.2012
comment
Да, мне нужно вызывать методы в обоих направлениях :( от ребенка к родителю и от родителя к ребенку...   -  person JavaRunner    schedule 10.10.2012


Ответы (4)


Вам нужно передать указатель this из класса A (или любого указателя на него) в класс B (возможно, в конструкторе), чтобы он знал, где он содержится, и, возможно, имеет член класса B, который является указателем на A . Затем вызовите метод экземпляра, как и любой другой.

Здесь нет наследования, поэтому некоторые другие примеры не будут работать.

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

class ParentClass; // Empty
class ChildClass
{
    ParentClass* myParent;
    // Body omitted
}

Убедитесь, что ChildClass.h включен в верхней части ParentClass.h и объявите это как обычно, и включите ParentClass.h в оба файла .cpp. И убедитесь, что вся реализация ChildClass находится в файле .cpp, и то же самое для ParentClass (каждый в своем, или нет, не имеет значения).

Здесь происходит то, что вы создаете циклическую ссылку для компилятора, но в ChildClass.h все, что вам нужно, это сказать компилятору "вот указатель" и все. Таким образом, вам не нужен «полный размер» класса, поэтому достаточно «пустого» предварительного объявления. К тому времени, когда файлы .cpp передаются, компилятор «знает» полный размер каждого из них и не выдает ошибок.

См. Часто задаваемые вопросы по C++ для получения дополнительной информации по этому вопросу.

person Kevin Anderson    schedule 10.10.2012
comment
Это отличное решение. Я пытался это сделать, но мой код не работает с ошибкой. Я обновил первое сообщение. - person JavaRunner; 10.10.2012
comment
Спасибо за ссылку. Вы действительно помогаете мне! Я пытаюсь сделать несколько яичек - и теперь это работает! Круто :) Спасибо! Это действительно то, что я искал! - person JavaRunner; 10.10.2012

Если A::callMeWhenLongProcessFinished() на самом деле единственная функция, которую вы хотите вызывать из B::longProcess(), вы можете просто передать указатель на ваш объект A в функцию:

void B::longProcess(A* object) {
    // long process
    object->callMeWhenLongProcessFinished();
}

Если настройка не так тривиальна, вы могли бы вместо этого B::longProcess() взять std::function<void()>, настроенную для вызова соответствующей функции, и вместо этого вызвать ее:

void B::longProcess(std::function<void()> callback) {
    // long process
    if (callback) {
        callback();
    }
}

void some_function() {
    A* aptr = get_A_object();
    B* bptr = get_B_object();
    bptr->longProcess(std::bind(&A::callMeWhenLongProcessFinished, aptr));
}

Чтобы вызовы выполнялись в разных потоках, потребуется отправить объект соответствующим потокобезопасным способом, но это совершенно отдельный вопрос.

person Dietmar Kühl    schedule 10.10.2012
comment
Не могу передать указатель... Не знаю почему, но вылетает с ошибкой :) (исходник доступен в первом сообщении) - person JavaRunner; 10.10.2012
comment
Если тип неизвестен, вам необходимо предварительно объявить его. Поскольку у вас есть циклическая зависимость в настройке, которую вы показываете, вам нужно предварительно объявить хотя бы один из указателей. Однако следует избегать циклических зависимостей. Передача std::function<void()> прекрасно прерывает цикл зависимости. - person Dietmar Kühl; 10.10.2012

Вызовите родительский метод в конце вашего дочернего метода:

void ClassB::longProcess() {
  // do your processing
  // ...

  callMeWhenLongProcessHasFinished();
}

Если по какой-то странной причине вы не можете изменить longProcess, напишите другой метод, который вызывает эти два метода последовательно.

person meagar    schedule 10.10.2012

Вам следует присоединиться в ClassB, чтобы ClassA не выполнял свой метод, пока ClassB не будет завершен. В качестве альтернативы просто вызовите метод класса A в конце метода класса B.

person Kirby    schedule 10.10.2012
comment
Но если я запускаю ClassB в другом потоке, как я могу вызвать метод из ClassA? - person JavaRunner; 10.10.2012