Добавьте методы, которые возвращают объекты того же класса в существующий класс в C++

Одним из способов добавления методов в класс без его изменения является наследование (например, Добавить метод в существующий класс C++ в другом файле). Однако это решение вызывает проблему с игрушечной программой ниже:

#include <vector>       

class c_A {
    public:
        std::vector<int> v;
        c_A one_element(void) {
            c_A res; 
            res.v = v;
            res.v.resize(1);
            return res;
        };
};

class c_Aext : public c_A { 
   // Methods here
};

int main () {
 
   c_Aext Aext;
   Aext.v = {0, 1, 2};
   c_Aext B = Aext.one_element(); // The problem
   return 0;
};

Каковы возможные решения этой проблемы? Кастинг? Удаление класса c_Aext и интеграция его методов в C_A? ...

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

1201ProgramAlarm 23.12.2020 19:54

@ 1201ProgramAlarm Выглядит интересно. Не могли бы вы написать это как решение?

Medical physicist 23.12.2020 19:56

Реализация ковариантных возвратов требует некоторой перезаписи. И есть множество вопросов, которые обсуждают это.

1201ProgramAlarm 23.12.2020 20:10

Найдите «конструктор виртуальной копии [C++]» и немного прочитайте.

Ulrich Eckhardt 24.12.2020 10:35
Стоит ли изучать 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
4
69
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Вы можете добавить свою собственную оболочку one_element() вместе с конструктором c_Aext, который примет c_A:

class c_Aext : public c_A { 
public:
    c_Aext() : c_A() { } // just call parent's default
    c_Aext(const c_A c) : c_A(c) { } // call parent's copy

    c_Aext one_element() { return c_Aext(c_A::one_element()); }
};

Полный скомпилированный пример здесь: https://ideone.com/auSoH4

Да, но это решение плохо работает, когда у c_A много методов.

Medical physicist 23.12.2020 19:50

@Medical вам нужно будет добавить только одну строку для каждой из функций c_A, которые выглядят следующим образом. Если у c_A будет меньше 30 таких функций, я не думаю, что это было бы слишком плохо.

scohe001 23.12.2020 19:53

Другой вариант с использованием интеллектуальных указателей и кастинга:

#include <vector>     
#include <memory>  

class c_A {
    public:
        std::vector<int> v;
        std::shared_ptr<c_A> one_element(void) {
            std::shared_ptr<c_A> res = std::make_shared<c_A>(); 
            res->v = v;
            res->v.resize(1);
            return res;
        };
};

class c_Aext : public c_A { 
   // Methods here
};

int main () {
 
   c_Aext Aext;
   Aext.v = {0, 1, 2};
   std::shared_ptr<c_Aext> B = std::static_pointer_cast<c_Aext>(Aext.one_element()); 
   return 0;
};

Кроме того, вы можете поискать CRTP

Если вы измените свой пример, вы можете получить что-то вроде:

#include <vector>
template < typename T>
class A: public T
{
    public:
        std::vector<int> v;

        A<T> one_element() {
            A<T> res;
            res.v = v;
            res.v.resize(1);
            return res;
        };
};

class Aext {
    public:
        void AnyOtherMethod(){ std::cout << "Other" << std::endl; }
};

int main () {
    A< Aext> aext;
    aext.v = {0, 1, 2}; 
    A< Aext> b = aext.one_element(); // The problem
    b.AnyOtherMethod();

    return 0;
}

Приведенный выше код не является полным CRTP, поскольку в нем нет операций приведения для доступа к классу расширения и т.д. См. связанную страницу википедии, где можно найти гораздо больше примеров того, как вы можете использовать ее для «настройки» вашего класса на лету и доступа к данным между такими классами в вашей «обратной» иерархии. Я бы не стал копипастить сюда все подряд.

Может быть, CRTP соответствует вашей цели дизайна или нет, но надеюсь, что это даст вам другую идею.

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