Производный объект в том же месте, что и базовый объект


У меня есть проблема, что у меня есть указатель на объект базового класса "элемент" в классе "список". В определенных ситуациях мне нужен элемент более специализированного типа "переход". Переход происходит от элемента. Но когда я делаю объект перехода от указателя к объекту элемента, указатель теперь отделен от моего списка классов матрицы.

Однако до сих пор в своих исследованиях я читал, что это способ создания производного объекта. Но это не то, что я имел в виду... Я хочу производный объект должен находиться в том же месте, что и указатель базового класса (указатель на класс элемента, "последний"). Как бы я должен это делать?

Пример кода:

#include <iostream>


class Element{

    public:

        Element(){
            member = 0;
        }

        virtual void set_member(int i){}

        void print_member(){
        std::cout << member << std::endl;
    }

    protected:
        int member;

};


class Transition: public Element{

    public:
        void set_member(int i){
            member = i;
        }

};


class List{

    public:

    List(){
        last = new Element;
    }

    Element*    get_last(){
            return last;
        }

    void print(){
        last->print_member();
    }

    private:
        Element* last;

};

int main(){

    List list;
    Element* last = list.get_last();
    last = new Transition; // creates completely new entity.
                           // I wanted to make a derived object
                           // from Element at the same location
                           // as the Elememt object though
    last->set_member(1);
    last->print_member(); // returns newly assigned value
    list.print(); // returns default value of Element type
                  // but I wanted itto return newly assigned value

}
4 2

4 ответа:

Element* last = list.get_last();
last = new Transition;
Теперь локальная переменная last не имеет ничего общего с переменной-членом last в классе List, поэтому поведение локальной переменной не будет влиять на переменную-член last. Вам может понадобиться метод сеттора, чтобы установить его обратно. Например:
class List {
public:
    void set_last(Element* e) {
        last = e;
    }

    // ...
};

А затем в main:

int main() {
    List list;
    Element* last = new Transition; // creates completely new entity.
    last->set_member(1);
    list.set_last(last);            // set it back to List
    list.print();
}

[EDIT]
Кстати: вы используете необработанный указатель в своем классе List, вам нужно обратить внимание на управление им. Например, вы должны delete его в деструкторе, а перед этим установить новый указатель на него, delete старый указатель и так далее. И вообще, принять идиому RAII с помощью интеллектуального указателя (например, std::unique_ptr) - лучшее решение.

Я хочу, чтобы производный объект находился в том же месте, что и указатель базового класса (указатель на класс элемента, "последний"). Как бы я должен это делать?

Позвольте мне понять, что вы пытаетесь сделать.

У вас есть:

Element* last = list.get_last();

В этой точке last указывает на допустимый объект, который является либо Element, либо подтипом Element.

Теперь вы хотите создать объект Transition и хотите, чтобы адрес этого объекта совпадал с адресом точек объекта last. куда?

Короткий ответ: Не делай этого

Длинный ответ:

Да, вы можете сделать что-то подобное, используя оператор placement new .

Тем не менее, вы должны знать все о местоположении памяти, прежде чем сможете его использовать.
  1. Вы должны знать, что в этом месте достаточно памяти для хранения объекта Transition.

  2. Жизнь объекта, который жил там, должна быть прекращена путем вызова его деструктор.

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

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

  5. Если память была выделена с помощью оператора new [], она должна быть освобождена с помощью оператора delete []. Если память была выделена с помощью равнины Оператор new, он должен быть освобожден с помощью простого оператора delete.

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

Прежде всего, как говорят другие.. Я думаю, что это нужно немного пересмотреть.

Но учитывая вопрос, который вы задали, поймите, что это:
Element*    get_last(){
        return last;
    }

Возвращает указатель на last (вещь, которую вы обновили), но вы не хотите изменить last, вы хотите изменить указатель на last. Это можно было бы сделать так:

Element**    get_last(){
        return &last;
    }

Это вернет указатель на указатель на элемент. Затем вы можете установить этот указатель на новый объект, но осознайте, что если вы это сделаете что вы только что слили память об элементе, который вы первоначально обновили. Все, что вы создаете , должно быть удалено (даже если вы идете с комментарием songyuanyao выше.. убедитесь, что вы удалили старый элемент в set_last и в деструкторе списка). Таким образом, вы получите:

Element ** last = list.get_last();
delete(*last); // Removes the old entity from memory.
*last = new Transition; // creates completely new entity.
Я не могу придумать ни одного обстоятельства, при котором я написал бы это.. мне действительно кажется,что вы нуждаетесь в некотором пересмотре.

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

Мое решение теперь таково:

#include <iostream>


class Element{

    public:

        Element(){
            member = 0;
            successor = 0;
        }

        virtual void set_member(int i){}

        virtual void set_successor(int successor){}

        void print(){
        std::cout << member << std::endl;
        std::cout << successor << std::endl;
    }

    protected:
        int member;
        int successor;

};


class Transition: public Element{

    public:
        void set_member(int i){
            member = i;
        }

        void set_successor(int i){
            successor = i;
        }

};


class List{

    public:

    List(){
        last = new Transition;
    }

    Element*    get_last(){
            return last;
    }

    void set_last(Element* new_last){
        last = new_last;
    }

    void print(){
        last->print();
    }

    private:
        Element* last;

};

int main(){

    List list;
    (list.get_last())->set_member(6);
    (list.get_last())->set_successor(8);
    list.print();

}