наследование — доступ к членам производного класса через указатель базового класса. Переполнение стека.

Есть ли способ получить общий доступ к членам производного класса через указатель базового класса? Или как-то иначе?

Допустим, у меня есть класс Shape. У меня есть классы Square и Triangle, которые наследуют его. Оба имеют своих личных членов, которые не имеют ничего общего друг с другом, поэтому нет смысла иметь их в базовом классе. Теперь, что если мне нужно записать класс в файл, но я не знаю, является ли класс Квадратом или Треугольником, до того момента, когда мне нужно будет записать его в файл?

Я пытался выяснить, как решить эту проблему. В худшем случае решением было бы записать данные как Square, так и Triangle в файл, добавить идентификатор (Triangle или Square) как для чтения, так и для записи, и создать небольшой синтаксический анализатор для объединения классов при загрузке данных. Это было бы неэффективно и трата времени.

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

4

Решение

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

  • идентификатор
  • все данные, которые должны быть сериализованы

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

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

8

Другие решения

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

class Shape{
public:
virtual int data() const = 0;
};
class Square: public Shape{
private:
int _member;
public:
virtual int data() const{
//do something with private members and return it
return _member;
};
};
3

Я думаю, что нет прямого способа снять эти накладные расходы. Обычно это делают две вещи. Прежде всего, объекту нужен механизм сериализации:

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

Простая часть сериализации:

class Shape{
public:
virtual void serialize( Datacontainer &o ) const = 0;
};

class Triangle: public Shape{
void serialize( Datacontainer &o ) const{
o.add('T');
o.add(corners);
}
std::vector<point> corners;
}

class Circle: public Shape{
void serialize( Datacontainer &o ) const{
o.add('C');
o.add(center);
o.add(radius);
}
point center;
double radius;
}

Во время сериализации вы можете сделать это с помощью базового класса Shape:

Shape *tri = new Triangle;
tri->serialize( dataContainer or file );

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

Добавьте следующую вещь ко всем вашим классам:

static Shape* createFrom( Datacontainer &o );

Например, Круг реализации:

Shape* Circle::createFrom( Datacontainer &o )
{
Circle *c = new Circle()
c->center = o.get();
c->radius = o.get();
}

Это позволяет нам создать бетон экземпляр, но у нас есть общий след функции для метода. Теперь можно реализовать очень простой конструктор, такой как этот:

class ShapeBuilder
{
public:
static Shape* createShape( Datacontainer& o )
{
char id = o.get();
swith(id){
case 'T':
return Triangle::createFrom(o);
case 'C':
return Circle::createFrom(o);
}
}
}
1

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

0

Наверное, лучший способ сделать что-то подобное. Основной шаблон заключается в том, что в базу можно поместить общий код, который гарантированно всегда будет одинаковым для каждого производного класса. Вещи, которые должны отличаться, включают виртуальную функцию, которую каждый производный класс реализует по-своему.

class Shape {
virtual void writeSerialData(std::ostream &) const = 0;
public:
void writeToFile(const std::string &filename) const {
std::ofstream outfile(filename); // filename.c_str() in C++03
writeSerialData(outfile);
if (!outfile.close()) {
// report the error
}
}
virtual ~Shape() {}
};

class Square : public Shape {
double length;
virtual void writeSerialData(std::ostream &out) const {
out << "Square{" << length << '}';
}
public:
Square(double l) : length(l) {}
};

Теперь у вас есть следующая проблема — как вы читаете объект обратно из файла, не зная заранее, какой это производный класс? Для этого вам нужен способ увидеть текст Square и либо (а) вызвать статическую функцию класса Square который знает, как интерпретировать данные или (b) создать экземпляр класса Square, предоставив ему данные для интерпретации. Прежде чем идти слишком далеко по этому пути, стоит изучить Boost Serialization или другие библиотеки сериализации.

0