2016-04-26 6 views
1

Как я могу сделать класс, который иногда только для чтения, а иногда и для записи? Один из вариантов - с getter/seters и флагом, который указывает, является ли объект доступным только для чтения, но это много накладных расходов. Я также хочу, чтобы это свойство readonly работало глубоко над объектом, убедившись, что все содержащиеся в нем объекты также доступны только для чтения или записи. Вот пример кода желаемого поведения, который я пробовал, но не смог добиться, используя const.Как сделать объект глубоко readonly

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

Пример кода: https://ideone.com/4cXyNF

class InnerClass { 
public: 
    InnerClass(int j) : j_(j) {} 
    int j_; 
}; 

class OuterClass { 
public: 
    OuterClass(int i, InnerClass& c) : i_(i), reference_(c), pointer_(&c) {} 
    int i_; 
    InnerClass& reference_; 
    InnerClass* pointer_; 
}; 

int main() { 
    InnerClass c(1); 

    OuterClass readwrite(2, c); 
    // Desire these 3 operations to work on the writable object 
    readwrite.i_ = 3; 
    readwrite.reference_.j_ = 4; 
    readwrite.pointer_->j_ = 5; 

    const OuterClass readonly(6, c); 
    // COMPILER ERROR: error: assignment of member 'OuterClass::i_' 
    // in read-only object 
    // readonly.i_ = 7; 
    // Desire this to be a compiler error, but it isn't 
    readonly.reference_.j_ = 8; 
    // Desire this to be a compiler error, but it isn't 
    readonly.pointer_->j_ = 9; 

    return 0; 
} 
+0

Ну почему это не приводит к ошибке компиляции, потому что вы ** не ** изменения указателя или ссылки (эти значения все еще указывают на одно и то же) .... Я думаю, вы могли бы использовать шаблонное решение (мне нужно было бы подумать об этом немного больше) – DarthRubik

+0

Вместо того, чтобы спрашивать, как это сделать Не могли бы вы объяснить, чего вы действительно пытаетесь достичь? То, что вы просите, довольно сложно сделать, и вполне вероятно, что ваше основное требование на самом деле имеет более простое решение. –

ответ

1

Это аналогичное решение, как некоторые еще уже размещены, но использует несколько иной подход:

class InnerClass { 
public: 
    InnerClass(int j) : j_(j) {} 
    int j_; 
}; 

template<bool readOnly> 
class OuterClass{ 
public: 
    OuterClass(int i, InnerClass& c) : i_(i), reference_(c), pointer_(&c) {} 
    int i_; 
    typename std::conditional<readOnly,const InnerClass&, InnerClass&>::type reference_; 
    typename std::conditional<readOnly,const InnerClass* const, InnerClass*>::type pointer_; 
}; 

int main(int argc,char** args){ 
    InnerClass c(1); 
    OuterClass<true> readonly(12,c); 
    //readonly.reference_.j_ = 1; //Error "reference_ is read only" 
    //readonly.pointer_->j_ = 1; //Error "pointer_ is read only" 
    OuterClass<false> write(12,c); 
    write.reference_.j_ = 1; 
    write.pointer_->j_ = 1; 
} 
+0

Интересный подход! К сожалению, у этой проблемы есть такая же проблема, как и ответ Pixelchemist в том смысле, что она не работает рекурсивно, если у нас есть InnerInnerClass и она должна совпадать с указателями и ссылками внутри InnerClass, если кто-то написал InnerClass, и я не могу изменить свою подпись. – AffluentOwl

+0

@AffluentOwl Если у вас нет контроля над декларацией ссылок и/или указателей, я не знаю, есть ли их ** **, что вы можете сделать ... по крайней мере, не то, что я могу видеть – DarthRubik

2

Если вы меняете пользователей к функциям, вы могут создавать постоянные перегрузки методов, подобных этому

class InnerClass { 
public: 

    explicit 
    InnerClass(int j) : j_(j) {} 

    int& j() { return j_; } 
    const int& j() const { return j_; } 
private: 
    int j_; 
}; 

class OuterClass { 
public: 

    OuterClass(int i, InnerClass& c) : i_(i), reference_(c), pointer_(&c) {} 

    int& i() { return i_; } 
    const int& i() const { return i_; } 

    InnerClass const& reference() const { return reference_; }; 
    InnerClass & reference() { return reference_; }; 

    InnerClass const* pointer() const { return pointer_; }; 
    InnerClass * pointer() { return pointer_; }; 

private: 
    int i_; 

    InnerClass& reference_; 
    InnerClass* pointer_; 
}; 

int main() { 
    InnerClass c(1); 

    OuterClass readwrite(2, c); 
    // Desire these 3 operations to work on the writable object 
    readwrite.i() = 3; 
    readwrite.reference().j() = 4; 
    readwrite.pointer()->j() = 5; 

    const OuterClass readonly(6, c); 
    // COMPILER ERROR: error: assignment of member 'OuterClass::i_' 
    // in read-only object 
    readonly.i_ = 7; 
    // Desire this to be a compiler error, and it is 
    readonly.reference().j() = 8; 
    // Desire this to be a compiler error, and it is 
    readonly.pointer()->j() = 9; 

    return 0; 
} 

Live on Coliru

+0

Прохладный. ... Я не понимал, что этот эффект можно сделать без шаблонов. – DarthRubik

+0

Это то же самое решение, что и Pixelchemist, и имеет те же проблемы, о которых я прокомментировал. А именно, это не работает, если есть также InnerInnerClass, а InnerClass имеет указатель на него, и у меня нет возможности модифицировать подпись InnerClass, потому что я не владею этим кодом, и это часть библиотеки, которую я с помощью. – AffluentOwl

2

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

class InnerClass { 
public: 
    InnerClass(int j) : j_(j) {} 
    int j_; 
}; 

class OuterClass 
{ 
    InnerClass& reference_; 
public: 
    OuterClass(int i, InnerClass& c) : i_(i), reference_(c) {} 
    int i_; 
    InnerClass & in() { return reference_; } 
    InnerClass const & in() const { return reference_; } 
}; 

Теперь ни i_, ни in().j_ перезаписываем в случае outer является const:

InnerClass i{ 1 }; 
OuterClass write(2, i); 
write.i_ = 3; // works 
write.in().j_ = 3; // works 
OuterClass const read(2, i); 
read.i_ = 3; // error! 
read.in().j_ = 3; // error! 
+0

Это довольно близко! Единственная проблема заключается в том, что я полагаюсь на возможность изменять интерфейсы всех классов во всем дереве объектов. Представьте, что есть InnerInnerClass, и у меня нет разрешения на модификацию интерфейса InnerClass, поскольку кому-то принадлежит этот код. – AffluentOwl

+0

@AffluentOwl: Интерфейс 'inner' не затрагивается. Это интерфейс «внешнего», который изменился (ссылка на внутреннюю закрытую и две функции доступа, добавленные там, где «const», а другая нет). – Pixelchemist

+0

Я говорю, чтобы предположить, что эти определения классов являются рекурсивными. Итак, представьте, что InnerClass имеет 2 члена 'InnerInnerClass & reference_' и' InnerInnerClass * pointer_', которые я также хотел бы прочитать только. – AffluentOwl

Смежные вопросы