Классы C ++, которые должны ссылаться друг на друга как в общем коде, так и в коде подкласса - PullRequest
2 голосов
/ 02 декабря 2011

У меня есть три класса, config, circuit и conn, которые держат указатели друг на друга, например:

struct config { /* ... */ };
struct circuit {
    config *cfg;
    /* ... */
};
struct conn {
    config *cfg;
    circuit *ckt;
    /* ... */
}

(Нет контроля доступа и интеллекта указателей, потому что эта программа находится на полпути к конвертации из C в C ++. Это сейчас не важно.)

Когда эти классы подклассы, все три из них всегда подклассы в группе:

struct foo_config  : config  { /* ... */ };
struct foo_circuit : circuit { /* ... */ };
struct foo_conn    : conn    { /* ... */ };

Кроме того, это инвариант времени выполнения, когда указатель cfg в foo_circuit или foo_conn будет всегда указывать на экземпляр foo_config, а указатель ckt в foo_conn будет всегда указывать на экземпляр foo_circuit. В настоящее время это выполняется с dynamic_cast и утверждениями. В настоящее время есть два разных foo s, но в будущем их может быть больше.

Можно ли упорядочить вопросы так, чтобы указатели cfg и ckt оставались доступными для методов универсальных классов circuit и conn и имели там общий тип, но в методах подклассов, эти указатели имеют соответствующий тип подкласса, и приведенный выше инвариант становится принудительным во время компиляции? Если так, то как? Если нет, что бы вы предложили мне сделать вместо этого?

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

1 Ответ

0 голосов
/ 02 декабря 2011

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

struct config { /* ... */ };

struct circuit {
private: 
    config *cfg;
protected:
    void set_cfg(config* _cfg) { cfg = _cfg; }
    config* get_cfg() { return cfg; }
};

template <typename CFG>
struct my_circuit : circuit {
public:
    // type specific setters/getters
    void set_cfg(CFG* _cfg) { circuit::set_cfg(_cfg); }
    CFG* get_cfg() { return dynamic_cast<CFG*>(circuit::get_cfg()); }
};

// below is how you define two different sets of subclasses:
struct foo_config  : config  { /* ... */ };
typedef my_circuit<foo_config> foo_circuit;

struct bar_config  : config  { /* ... */ };
typedef my_circuit<bar_config> bar_circuit;

// example usage
foo_config foo_cfg;
foo_circuit foo_ckt;
bar_config bar_cfg;

foo_ckt.set_cfg(&foo_cfg); // okay
foo_ckt.set_cfg(&bar_cfg); // not okay, compiler error!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...