Одним из способов добавления методов в класс без его изменения является наследование (например, Добавить метод в существующий класс 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 Выглядит интересно. Не могли бы вы написать это как решение?
Реализация ковариантных возвратов требует некоторой перезаписи. И есть множество вопросов, которые обсуждают это.
Найдите «конструктор виртуальной копии [C++]» и немного прочитайте.
Вы можете добавить свою собственную оболочку 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 вам нужно будет добавить только одну строку для каждой из функций c_A
, которые выглядят следующим образом. Если у c_A
будет меньше 30 таких функций, я не думаю, что это было бы слишком плохо.
Другой вариант с использованием интеллектуальных указателей и кастинга:
#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 соответствует вашей цели дизайна или нет, но надеюсь, что это даст вам другую идею.
Возможно, вы сможете использовать ковариантный возвращаемый тип с некоторыми изменениями, но для этого требуется, чтобы возвращаемое значение было указателем или ссылкой.