Как я могу вернуть экземпляр производного класса из функции базового класса?

Допустим, у меня есть два класса. base и derived_from_base. base будет содержать множество функций, которые будут управлять определенными аспектами base и будут использоваться производными классами. Однако я хотел бы сделать так, чтобы можно было связывать вызовы этих функций.

class data_class {};

class base {
public:
  base* add_data(data_class* data) {
    m_Data = data;
    return this;
  }

private:
  data_class* m_Data;
};

class derived_from_base : public base {
  void custom_function() {}
};

int main() {
  data_class* someData = new data_class();

  // "myClass" has none of the data after this return
  derived_from_base* myClass = derived_from_base().add_data(someData);

  derived_from_base* myOtherClass = derived_from_base();
  // The data is available in this class now.
  myOtherClass.add_data(someData);
}

Я попытался передать указатель производных классов this на базовый класс, но это дало те же результаты.

Почему вы ожидаете, что «эта переменная» будет содержать какие-либо данные? Вы передаете его копию базовому классу, который затем сохраняет другую копию в переменной-члене? Кроме того, что бы вы ни ожидали от myClass, это висячий указатель, поскольку временный объект удаляется. Фактически, судя по всему, компилятор должен даже предупредить вас, потому что вы присваиваете указатель базового класса указателю производного класса, что небезопасно.

Christian Stieber 16.04.2024 19:34

В моем реальном коде я передаю указатель на функцию add_data. Однако, когда я отделяю вызов add_data, данные доступны в myClass. Я надеялся, что смогу объединить эти звонки воедино. Я отредактирую свой вопрос, включив в него эти две вещи.

Nepgupmixpro 16.04.2024 19:39

Вы могли бы сэкономить время на вводе текста, назвав свой класс derived вместо derived_from_base (поскольку нет никакой двусмысленности с соответствующим названием базового класса base).

JaMiT 16.04.2024 22:48

Похоже, у вас две проблемы: 1) приведение указателя, возвращаемого add_data, к указателю derived_from_base и 2) наличие висячего указателя на временный объект. О чем из этого вы хотели спросить? Первое является ошибкой времени компиляции, поэтому его необходимо устранить до второго. (Если только вы не изменили свой код на base* myClass = base().add_data(someData);, который, вероятно, демонстрирует то же самое «не имеет никаких данных после этого возврата» без необходимости в производном классе. Наличие двух таких строк с разными данными еще более вероятно продемонстрирует висячий указатель .)

JaMiT 16.04.2024 22:56

В будущем, пожалуйста, напишите минимальный воспроизводимый примерs. Вы предоставили код, который не компилируется, а затем рассказали об ошибках времени выполнения в нем.

Yakk - Adam Nevraumont 18.04.2024 02:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Иногда подобные вещи достигаются с помощью идиомы CRTP.

В вашем случае это будет выглядеть примерно так:

class Base {
private:
  // ...
public:
  Base* add_data(data_class* data) {
    // implementation ...
  }
};

template <typename T>
class BaseCRTP : public Base {
public:
  T* add_data(class_data* data) {
    return static_cast<T*>(Base::add_data(data));
  }
};

class Derived : public BaseCRTP<Derived> {
public:
  void custom_function() {}
};

int main() {
  Derived derived;
  derived.add_data(new class_data())->custom_function();
}

Это не лучший стиль по нескольким пунктам. Во-первых, было бы лучше возвращать ссылку, а не указатель. Так что *this, а не this.

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

Функция C++23 «вывод этого» должна упростить этот код, чтобы BaseCRTP больше не требовался.

Remy Lebeau 16.04.2024 21:39

@RemyLebeau Да, ты прав. Я бы отредактировал это в ответ, но OP помечен как C++20.

bitmask 16.04.2024 21:49

ох, я этого не заметил, спасибо

Remy Lebeau 16.04.2024 21:50

Другие вопросы по теме