У меня есть следующая проблема дизайна:
у меня есть Resource
с двумя видами аксессуаров:
Access
)Const_access
), но вы могли бы сказать c1 = c2, и тогда c1 получит доступ к c2.При условии Resource
Я должен добиться следующего механизма копирования:
Access->Access: deep copy
Access->Const_access: deep copy
Const_access->Access: deep copy
Const_access->Const_access: shallow copy
Я стремлюсь написать Access
чтобы Const_access
сможет использовать именно const
функции в Access
,
Моя текущая реализация имеет недостатки, используя:
class Access {
public:
Access(const Access&); // deep copy
void method(const Access&);
void const_method() const;
protected:
Resource res;
};
class Const_access : public Access{
private:
void method(); // only declaration
public:
Const_access(const Const_accesss&); // shallow copy
explicit Const_access(const Access&); // deep copy
};
но здесь Const_access ca; ca.Access::method()
все еще работает, и я должен вручную скрыть неконстантные методы доступа. Я пробовал защищенное или частное наследование, но это запрещает гибкость для Access&
обрабатывать Const_Access&
тоже.
Каково было бы правильное решение этой проблемы?
То, что вы говорите, противоречиво.
С одной стороны, вы хотите запретить такие вещи, как:
Const_access foo;
foo.modify();
но с другой стороны, вы хотите разрешить такие вещи, как:
void bar(Access& a) {
a.modify();
}
Const_access foo;
bar(foo);
Это не имеет смысла.
Более логичные отношения — перевернуть структуру наследования:
class Const_access {
public:
Const_access(const Const_access&); // shallow copy
void const_method() const;
protected:
Resource res; // or perhaps a reference-counted pointer?
};
class Access: public Const_access {
public:
Access(const Access&); // deep copy
explicit Access(const Const_access&); // deep copy
void method();
};
Единственное, что он не дает, это глубокая копия при преобразовании Access
к Const_access
,
Ваша функция method()
имеет публичную видимость в базовом классе, но приватно в производном классе. Это нарушает принцип подстановки Лискова. Производный класс должен расширять, а не сокращать базовый класс.
Решение состоит в том, чтобы не нарушать этот принцип. Например, сделать наследство в class Const_access
частный или защищенный, или обеспечить реализацию method()
в class Const_access
,
Эта проблема может быть просто решена с помощью так называемой ленивая оценка:
создание частного клона ресурсов класса только тогда, когда функция-член хочет его изменить. Доступ к ресурсам с правами на чтение и чтение доступен с помощью частного наследования.
Таким образом, LSP соблюдается: Obj
теперь безупречно публично унаследовал от Const_obj
при необходимости вообще.
Есть ссылка для полного ответа.