After reading Effective C++ by Scott Meyers, the following is a solution:
define a template which does a lazy evaluation (with reference counting):
class Obj : private lazy<Obj_data>{};
and the lazy stores the Obj_data privately, has protected accessors, one for modification, one for read-only access.
The modifier accessor first deep-copies the Obj_data
if necessary, then hands over the reference to the data. The read-only accessor just returns a const reference.
The overall cost of this is storing 2 extra pointers (one for the data and one for the counter) and a counter.
Implementation is something like this:
class lazy{
protected:
lazy(const lazy&obj){lazy_copy(obj);}
//(the required constructors, operator= ...)
// accessors:
const Obj_data& data() const {return *od;}
Obj_data& mod_data() {make_private(); return *od;}
private:
void lazy_copy(const lazy& obj);
void make_private(); // this does the actual deep-copy, as late as possible.
private:
counter*;
Obj_data* od;
};
So, reading and modifying an attribute of Obj
goes
void Obj::method(){
cout << data().some_attribute; // simple read
mod_data().i = 10; // simple modify
const Obj_data& const_d = data(); // assignable for lots of read-outs
Obj_data& var_d = mod_data(); // assignable for lots of modifications.
}
Note that you can only use data()
in a const
member as mod_data()
is a non-const function in the class, so this solution is completely safe with little overhead.
Theory background: the desired behaviour in the question is an implementation detail, does not concern the client. Therefore we solve it by private inheritance.