установить базовый объект производного объекта? - PullRequest
1 голос
/ 21 июня 2011

Это основной концептуальный вопрос. Если у меня есть класс, который является производным, который наследуется от Base, и я создаю экземпляр нового объекта Derived, могу ли я установить его базовый объект для определенного объекта Base по своему выбору, чтобы все вызовы методов базового класса перенаправлялись на этот конкретный базовый объект?

как то так:

class Base
{
protected:
    string name;
public:
    Base(string n) { name = n}
    void doSomething(){cout << name << "\n";}
};

class Derived : public Base
{
public:
     Derived(string n) : Base(n) {}

int main()
{
    Derived* d = new Derived("original base"); //create a derived
    d->doSomething(); // prints "original base"
    Base* rB = new Base("replacement base"); // create a new base object
    ((Base*) d) = rB; // replace the base object of d with a new one (pretend code)
    d->doSomething(); // prints "replacement base"

    return 0;
}

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

Возможно ли это в C ++? Мы можем вырезать полученную информацию из объекта, поэтому можем ли мы отделить и заменить компоненты в цепочке наследования?

Зачем мне это делать?

Рассмотрим миксиновые лилии: (опять же, простите за синтаксические ошибки)

template <class T> class MyMixin : public T
{
public:
    MyMixin(T desiredBaseObject)
    { 
        // do something to set the desired base 
        // object of this class to desiredBaseObject.
    }
};

RandomClass1 dog(int i = 0);  
RandomClass2 cat(double i = 0.0);
MyMixin< RandomClass1 > mixin1(dog);
MyMixin< RandomClass2 > mixin2(cat);

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

Спасибо за ответы. Поскольку мы можем отрезать производную часть объекта, кажется, что база и производная информация живут отдельно. Может ли кто-нибудь прокомментировать это? Можем ли мы получить доступ к какой-нибудь внутренней таблице, например к vtables, о которой я так много слышал (я ничего не знаю об этом типе вещей, так что, возможно, это не применимо), и выполнить это?

@ * Benoît 1019 *

Не могли бы вы объяснить, почему только 1 и 4 работают, а 2 и 3 - нет? базовый класс { защищенный: std :: string name; общественности: База (std :: string n) { имя = n; } * * Тысяча двадцать-один

    virtual void doSomething()
    {
        cout << name << "\n";
    }
};

class Derived : public Base
{
public:
    int x;
    Derived(std::string n) : Base(n)
    {
        x = 5;
    }

    void printX()
    {
        cout << "x = " << x << "\n";
        x++;
    }
};


Derived* d1 = new Derived("original 1");
d1->doSomething();
d1->printX();
Base* rb1 = new Base("new 1");
*static_cast<Base*>(d1) = *rb1;
d1->doSomething();
d1->printX();
cout << "\n\n";

Derived d2 = Derived("original 2");
d2.doSomething();
d2.printX();
Base b2 = Base("new 2");
static_cast<Base>(d2) = b2;
d2.doSomething();
d2.printX();
cout << "\n\n";

Derived d3("original 3");
d3.doSomething();
d3.printX();
Base b3("new 3");
static_cast<Base>(d3) = b3;
d3.doSomething();
d3.printX();
cout << "\n\n";

Derived d4("original 4");
d4.doSomething();
d4.printX();
Base b4("new 4");
*static_cast<Base*>(&d4) = *&b4;
d4.doSomething();
d4.printX();
cout << "\n\n";

это напечатает:

оригинал 1 х = 5 новый 1 х = 6

оригинал 2 х = 5 оригинал 2 х = 6

оригинал 3 х = 5 оригинал 3 х = 6

оригинал 4 х = 5 новый 4 х = 6

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

Ответы [ 7 ]

3 голосов
/ 21 июня 2011

Нет.Если вам нужно сделать это, вы должны использовать композицию, а не наследование.

(Я отвечаю на более общий вопрос - в вашем конкретном случае, когда вы просто хотите изменить строку, вы можете просто изменить name из производного класса)

3 голосов
/ 22 июня 2011

Я не спрашиваю, почему вы хотите это сделать, но совершенно безопасно делать это, если только ваше наследство не нарушает отношения ISA (например, Derived является ограниченным подмножеством Base, например, Square не является Rectangle, поскольку возможноизменить размер только одного измерения прямоугольника, но невозможно сделать это с квадратом).

*static_cast<Base*>(d) = *rB;

(работает также со ссылками)

или вы можете написать небольшую функцию (вы найдете многофункций, выполняющих это):

template<typename T>
T& assign(T& to, const T& from)
{
  return to = from;
}

assign<Base>(*d, *rB);

и в любом случае, вы делаете это каждый раз, когда перегрузите / переопределите оператор =

Derived& operator=(const Derived& other)
{
  // prettier than the cast notation
  Base::operator=(other);

  // do something specific to Derived;
  this->name += " (assigned)";

  return *this;
}
1 голос
/ 22 июня 2011

Вы можете объединить наследование и состав:

class A {
    string name;
public: A(const char* s) : name(string(s)) {}
        virtual void m() { cout << name << endl; }
};

class B : A {
public:
    B(const char* s) : A(s), a(0) {}
    void m() { if (a) a->m(); else A::m(); }
    A* a;
};

int main() {
    B b("b");
    b.m(); // prints b
    b.a = new A("a");
    b.m(); // prints a
}
1 голос
/ 22 июня 2011

Наследование является свойством типов , а не объектов .Тип «Производный» наследуется от типа «База».Вы можете сделать объекты типа «Производные» (Derived x;), а также вы можете создать объекты типа «База» (Base y, если это не запрещено), но каждый из этих объектов завершен,полноценные объекты без «заменяемых частей».

Точка наследования типа заключается в том, что объект типа «Производные» можно трактовать , как если бы это был объект типа «Base"(если вы ссылаетесь на него по ссылке или по указателю), то есть если у вас есть функция void foo(Base & b);, то вы можете вызвать foo(x).Но foo может получить доступ только к тем частям x, которые унаследованы от "Base"!

1 голос
/ 22 июня 2011

Наследование = IS A отношение.

Состав = HAS A отношение.

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

0 голосов
/ 22 июня 2011

номер

Зачем тебе это делать? Если предполагается, что два деривата имеют одинаковую базу, так что модификации в одном деривате обнаруживаются в другом, у вас должен быть какой-то указатель на базу в каждом производном, создавая эту композицию, а не наследование.

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

В качестве альтернативы, вы можете создать новый конструктор для Derived, который будет принимать Base и Derived и получать информацию Base из Base и информацию Derived из Derived.

0 голосов
/ 21 июня 2011

Нет, вы не можете этого сделать.

У вас есть (как минимум) пара опций:

  • Создать новый объект Derived, параметризованный смесью параметровиз двух объектов, которые вы хотите объединить.
  • Создайте несколько методов установки в классе Base, которые позволят вам изменить его параметры / состояние позднее в его жизненном цикле.
...