Reputation: 11
I have a class that looks somewhat like this:
class S
{
public:
int* data;
S() : data(new int[10]) {}
};
The constructor allocates the memory of 10 integers, and the default copy constructor as expected merely copies the pointer itself rather than the content.
Even if there is an instance of S
that has const
modifier, I can modify the data that data
points to, since that data itself does not have const
modifier. I could avoid this by making data
private and only allowing write access via a non-const
method like so:
class S
{
private:
int* data;
public:
S() : data(new int[10]) {}
int& operator(size_t i)
{
return data[i];
}
const int& operator(size_t i) const
{
return data[i];
}
};
But now I can use the copy constructor to circumvent the const
ness of the instance of S
like so:
void main()
{
const S a; // Allocates memory
S b(a); // Also points to memory allocated for a
b(1) = 3; // Writes to a even though it is not supposed to be mutable
}
What would be an elegant way to solve this problem (potentially using templates)?
const S
should not be mutable at all using only this instance.const S
and an S
should be creatable via a copy constructor given an instance of S
such that the const
instance cannot modify the data, but the non-const
instance can.Upvotes: 1
Views: 209
Reputation: 308206
It is possible to know in the copy constructor if the object being copied is const
by providing two different copy constructors, one which takes a const
parameter and one which does not. The compiler will select whichever version matches the passed parameter. Set a flag in the constructor so it can throw an error when a non-const operation is performed.
The best way to avoid the leaked memory shown in the question is to used a smart pointer like std::shared_ptr
rather than a raw pointer. Unfortunately shared_ptr
is meant for single objects, not arrays; workarounds are possible as in this StackOverflow question. I'm not going to try to solve this now, the code below still has the leak.
To be complete you should follow the Rule of Three and provide an operator=
and destructor as well. I left this as an exercise for the reader.
class S
{
private:
int* data;
bool is_const;
public:
S() : data(new int[10]), is_const(false) { data[1] = 42; }
S(const S& other) : data(other.data), is_const(true) {}
S(S& other) : data(other.data), is_const(false) {}
int& operator()(size_t i)
{
if (is_const)
throw std::logic_error("non-const operation attempted");
return data.ptr[i];
}
const int& operator()(size_t i) const
{
return data.ptr[i];
}
};
See it in action: http://ideone.com/SFN89M
Upvotes: 1
Reputation: 32742
Delete the copy constructor (and assignment operator) for S
. Create a new proxy class (SCopy
) that holds a pointer to an S
object (which is passed in to the constructor for SCopy
). SCopy
would then implement the const int &operator() const
and not the non-const version.
This would then allow you to implement a destructor in S
that would free the memory you're currently leaking.
Upvotes: 0