частичный класс в переполнении стека

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

Вот что я попробовал:

Я создал библиотеку, которая содержит класс A:

A_p.h содержит частную часть класса A

void PrivateMethod(int i);

хиджры публичный заголовочный файл:

class A
{
public:
A();
virtual ~A();
// other public members
private:
#ifdef A_PRIVATE
#include "A_p.h"#endif
};

a.cpp

#define A_PRIVATE
#include "A.h"
A::A() {}
A::~A() {}
void A::PrivateMethod(int i) { }

Затем я создал консольный проект Win32, который включает публичный заголовок (A.h) и ссылки на файл .lib.

Кажется, все работает, но меня интересуют любые подводные камни на этом пути. Кто-нибудь может уточнить это?

1

Решение

Абстрактные классы позволяют вам объявлять открытый интерфейс, но иметь личные данные и функции путем создания подкласса абстрактного класса.

Основная причина, по которой это не будет работать так, как вы описываете, и, следовательно, не поддерживается в стандарте C ++, заключается в том, что предложенное вами публичное объявление делает невозможным определение размера A, Публичная декларация не показывает, сколько места необходимо для личных данных. Поэтому код, который видит только публичное объявление, не может быть выполнен new A, не может выделить место для определения массива Aи не может делать арифметику с указателями на A,

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

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

Затем в отдельном приватном коде вы объявляете и определяете класс B что происходит от A, Вам нужно будет предоставить способы создания и уничтожения объектов, вероятно, с помощью публичной «новой» функции, которая возвращает указатель на A и работает, вызывая частную функцию, которая может видеть объявление B и публичная функция «удаления», которая принимает указатель на A и работает, вызывая частную функцию, которая может видеть объявление B,

1

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

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

Поскольку это UB, может показаться, что он работает, но попробуйте объявить virtual метод в частном разделе, и вы, скорее всего, столкнетесь с некоторыми видимыми проблемами.

6

Есть 3 хороших способа скрыть такую ​​информацию:

  1. только вперед объявить твой класс. Это работает только в том случае, если оно просто передается через клиентский код (через указатели и / или ссылки), и только используемый внутри вашей библиотеки Ваша библиотека должна будет предоставить фабричные функции или аналогичные для возврата указателя / ссылки в первую очередь, клиент никогда не сможет вызвать new или же delete

  2. выставлять абстрактный базовый класс, и снова предоставить фабричные функции (которые создают конкретный производный класс, видимый только внутри вашей библиотеки)

  3. использование Pimpl

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


Что касается того, как & почему нарушение ODR ломает вещи на практике:

  • библиотека и ее клиентский код могут иметь разные мнения о размере экземпляров. Это может вызвать проблемы с распределением / освобождением и т. Д.
  • они также могут ожидать различные смещения элементов данных, макеты vtable и т. д.
    • PS. встроенные методы считаются клиентским кодом для этих целей, так как они не обновляются путем добавления новой динамической сборки библиотеки
    • PPS. более новые оптимизаторы могут быть в состоянии встроить некоторые из линии методы без вашего ведома
1
По вопросам рекламы [email protected]