问题
I have the following problem:
I have a class which should do this:
Obj o;
Obj o1(o), o1=o; // deep-copies
const Obj c(o), c=o; // deep-copies
const Obj c1(c), c1=c; // shallow-copies
Obj o2(c), o2=c; // deep-copies
How can I do this preferably without inheritance? (I mean I would do Const_obj
inheriting from Obj
otherwise.)
EDIT:
Using o.clone()
directly is not an option because then I could easily introduce bugs by accidentally not cloning.
EDIT:
Finally, there is a proper, complete solution with lazy evaluation using the idea from Effective C++ by Scott Meyers. Check out my answer below.
回答1:
No, you can't.
- A constructor can not be cv-qualified, so you can't force it to construct a const object.
- The return type of a function (including operators) is not a part of it's signature, so you can't overload a function with just changing it's return type.
Also, if it was possible, I would find it really confusing. Just make methods that suit your needs, and name them in an unambiguous way.
回答2:
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.
回答3:
You can, in part, with a dummy argument:
class C {
public:
struct NonStandardCopy { };
C (const C &) {
// "ordinary" copy constructor with default behavior
}
C (const C &, NonStandardCopy) {
// "other" "copy" constructor
}
};
C c = c1; // default
C c (c1); // default
C c (c1, C::NonStandardCopy ()); // non-default
EDIT: A clone-only approach could be what you want (together with move semantics the performance hit might not be too large):
class C {
private:
struct DeepCopy { };
struct ShallowCopy { };
C (const C &) = delete;
C (const C &, DeepCopy) {
// deep copy
}
C (const C &, ShallowCopy) {
// shallow copy
}
public:
// move constructor
C (C && other) = default;
const C clone () const { // 1
// shallow copy
return C (*this, ShallowCopy ());
}
C cloneToNonConst () const { // 2
// deep copy
return C (*this, DeepCopy ());
}
C clone () { // 3
return cloneToNonConst ();
}
};
C o;
C o1 = o.clone (); // call 3
const C o2 = o1.clone (); // call 3
const C o3 = o2.clone (); // call 1
C c4 = o3.cloneToNonConst (); // call 2; o3.clone () will give error
来源:https://stackoverflow.com/questions/13343718/can-i-write-different-copyctor-for-const-and-non-const-instances