В С++, как получить доступ к атрибутам из подкласса объекта, который когда-то был создан как подкласс, но затем скопирован в суперкласс?

Рассмотрим код ниже (примечание: после справедливой критики я переформулировал вопрос):

#include <vector>
using std::vector;

class DataSuper {
    public:
        DataSuper() {}
};

class DataSub : public DataSuper {
    public:
        int a;
        DataSub() {}
};

class Super {
    public:
        DataSuper data;
        Super() {}
};    

class Sub : public Super {
    public:
        Sub(DataSub i) {
            data = i;
        }
        
        void test() {
           // I would like to print the value of data.a
        }
};    

int main(int argc, char *argv[]) {
    DataSub dataSub;
    Super* s = new Sub(dataSub);
    s->test();
    delete(s);
    return 0;
}

Super имеет экземпляр DataSuper с именем data. Sub, подкласс Super, имеет тот же объект data, но является экземпляром DataSub, который наследуется от DataSuper.

По сути, я хотел бы получить доступ к data.a из класса Sub. Я знаю, что могу сделать это с помощью data в качестве указателя, а затем использовать dynamic_cast, но не уверен, что это хорошая практика.

Есть ли способ избежать этого БЕЗ использования data в качестве указателя?

Ваше использование static_cast ошибочно. Super::data не является экземпляром DataSub. Это экземпляр DataSuper. Сказать компилятору обработать его так, как будто это DataSub, который занимает больше места, значит напрашиваться на неприятности. Вам нужно переосмыслить свой дизайн.

Nathan Pierson 17.02.2023 23:32

Кроме того, отсутствие виртуального деструктора приводит к неопределенному поведению.

Sam Varshavchik 17.02.2023 23:33

В этом коде есть и другие проблемы, кроме потенциальных утечек памяти. godbolt.org/z/cqqv3WK7v

Retired Ninja 17.02.2023 23:33

Ошибка сегментации и утечка памяти — ортогональные понятия. Кажется, вы их путаете. Ошибка Seg возникает из-за доступа к ресурсу, которого у вас нет, а утечка памяти возникает из-за того, что вы не убираете ресурсы, которые у вас есть. Сбой seg может привести к утечке памяти, если программа выйдет из строя до того, как она сможет выделить ресурсы, но у вас есть более серьезные проблемы, чем утечка, если программа дает сбой.

user4581301 17.02.2023 23:36

Есть ли способ убедиться, что объект является экземпляром DataSuper без static_cast?

Chicoscience 17.02.2023 23:36

Есть ли способ убедиться, что объект является экземпляром DataSuper без static_cast? Да. И, если это не просто какое-то тупое программирование судоку, это то, что вы должны сделать. (Но он будет резаться.)

Eljay 17.02.2023 23:37

Хорошо, это не так, просто я хотел бы сделать это без использования указателей, и я совершенно не знаю, как это сделать. Будет ли это работать, имея только данные в суперклассе, но имея конструктор по ссылке?

Chicoscience 17.02.2023 23:39

Прямо сейчас, как и было задумано, Super::data всегда является экземпляром DataSuper и никогда экземпляром DataSub, независимо от того, какие приведения вы делаете. Если вы хотите, чтобы фактический тип Super::data менялся, вам нужен уровень косвенности, например, использование std::unique_ptr<DataSuper> или подобного.

Nathan Pierson 17.02.2023 23:39

Показанный код предполагает, что объекты в C++ будут работать так же, как в Java или C#. C++ — это не Java или C#, и объекты не работают таким образом на фундаментальном уровне.

Sam Varshavchik 17.02.2023 23:39

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

Yksisarvinen 17.02.2023 23:40

Тогда я собираюсь перефразировать этот вопрос по-другому.

Chicoscience 17.02.2023 23:45

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

Eljay 17.02.2023 23:48

Нет проблем с сохранением их конфиденциальности — общедоступный пример — это просто способ упростить пример кода.

Chicoscience 17.02.2023 23:51

Связанный: Что такое нарезка объектов? Конструктор Sub нарезает свой i параметр при назначении его члену Super::data

Remy Lebeau 18.02.2023 00:55
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел &quot;Заголовок&quot; в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
2
14
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Super::data не DataSub, но вы относитесь к нему как к Super::data.

Если вы хотите разделить Super между вашими классами Sub и data, вам нужно будет использовать указатель. Например:

class Super
{
public:
    std::unique_ptr<DataSuper> data;
    Super(std::unique_ptr<DataSuper> data) : data{std::move(data)} {}
};

class Sub : public Super
{
public:
    using Super::Super;

private:
    // Use this if you need to treat data as a DataSub
    DataSub& dataSub()
    {
        return static_cast<DataSub&>(*data);
    }
};

int main()
{
    std::unique_ptr<Super> s = std::make_unique<Sub>(std::make_unique<DataSub>());
}

Демо


Если вы хотите избежать дополнительного выделения для Sub, которое для этого требуется, вы можете изменить направление владения. То есть, пусть DataSub передает указатель, не владеющий Super, на конструктор Sub, и Sub::data владеет объектом:

class Super
{
public:
    Super(DataSuper* data) : data{data} {}
    
private:
    DataSuper* data;
};

class Sub : public Super
{
public:
    Sub() : Super{&data} {}

private:
    DataSub data;
};

int main()
{
    std::unique_ptr<Super> s = std::make_unique<Sub>();
}

Демо

Обратите внимание, что этот подход немного менее безопасен, чем первый подход, поскольку Sub еще не инициализирован, когда инициализируется подобъект Super Super::data. Если вы попытаетесь использовать объект, на который указывает Super, в конструкторе Super, вы быстро попадете в страну неопределенного поведения. То же самое касается деструктора Sub::data. Super::~Super уничтожается до того, как тело data будет выполнено, поэтому попытка доступа к объекту, на который указывает Super, из тела деструктора Super::data также приведет к неопределенному поведению.

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

Chicoscience 18.02.2023 03:26

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