Search code examples
c++inheritanceencapsulation

C++: Semantics for read-only and write-only versions of a class


In my program, I have a data structure called Foo. One part of the program constructs Foo from a set of parameters and so need access to mutative methods. Another part of the program will only read from Foo and so should not have access to mutative methods.

What is the best way to accomplish this, or am I taking the wrong view of how classes should work? I've used this way in the past:

class FooR {
public:
    int read() { return x; }
protected:
    int x;
};

class FooRW : public FooR {
    void mutate(int nx) { x = nx; }
};

But this way feels like you're abusing inheritance and I think it might cause confusion to have two very similarly named classes in two parts of the program. Are there better ways? Can you use const in some tricky way?

EDIT: @jens pointed out that using const references in the read-only part of the program should work, which made me realize it's important to know that my data structure is actually a tree, something like:

class Foo {
public:
    void splitNode();
    Foo *getChild(int n) const; // const Foo?
private:
    Foo *children[2];
};

Is there a way of forcing a const Foo to return only const references to its children?


Solution

  • The parts of your program which do not need to change the object should just usr const references, and mark the non-mutating member functions as const.

    class FooR {
    public:
        int read() const { return x; }
        void mutate(int nx) { x = nx; }
    private:
        int x;
    };
    
    void f1(FooR const& obj)
    {
        int x = obj.read();
        // cannot use obj.mutate(x+1);
    }
    void mutatingFunction(FooR&);
    

    You could do the construction in a factory or build, and return a std::unique_ptr<FooR const> or a std::shared_ptr<FooR const> to prevent anybody else from using the mutating interface.

    The same approach works for the tree. Declare a const-overload returning Foo const*, and a non-const overload returning a non-const pointer. The later one cannot be used from const references.

    class Foo {
    public:
        void splitNode();
        Foo const* getChild(int n) const;
        Foo* getChild(int n); 
    private:
        Foo *children[2];
    };