John Mescard
John Mescard

Reputation: 11

Pointer members of const struct

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 constness 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)?

Upvotes: 1

Views: 209

Answers (2)

Mark Ransom
Mark Ransom

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

1201ProgramAlarm
1201ProgramAlarm

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

Related Questions