Readonly class variables in C++

Feb 22, 2011 at 11:53am
closed account (DEU9GNh0)
One of C++ lacks in defining a class is absence of read-only variable members, something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class A
{
public:
   readonly int x; // x can not modify out of this class     

   void f()
   {
      x = 100; // Correct    
   }
}

int main()
{
   A a;
   a.x = 50; // error !

   return 0;
}



The popular and standard solution is using Setter/Getter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class A
{
    int x;

public:
    void setX(int x)
    {
        this->x = x;
    }

    int getX() const // The popular way is returning value of the private variable  
    {
        return this->x;
    }
};



But, there is another solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

class A
{
    int _x; // Private variable, it can modify in this class directly    

public:
    A() : x(_x) // Bind reference variable x to _x
    {
    }

    void setX(int x)
    {
        this->_x = x;
    }

    const int &x; // Constant variable
};

// Usage:

int main()
{
    A a;

    a.setX(50);

    cout << a.x << endl; // Correct

    a.x = 12; // Error !

    return 0;
}



Good luck
Masoud
Last edited on Feb 22, 2011 at 11:54am
Feb 22, 2011 at 3:28pm
Yes, though one of my pet peeves about C++ is that the moment you introduce a reference variable (const or not) as a data member of a class (struct) or even just a const non-reference, the class becomes copyable but not assignable. I don't understand the use of such a thing.

Because of that, I tend to prefer the get and set methods, however, as object oriented programming is supposed to model data flow, not the data itself, use of set/get methods generally implies that ownership of the data being set/get is in the wrong place.

Feb 22, 2011 at 7:29pm
closed account (zb0S216C)
x = 100;

This is not read-only. This is writing. A read-only variable( or const ) cannot change it's value. It's like R.O.M( Read-Only Memory ).

A way to make sure a variable never changes it's value: static const int Variable( 5 ); .
Feb 22, 2011 at 9:09pm
I think you somewhat missed the point, Framework. :/

The point is that C++ doesn't have a keyword that permits a variable to be read but not modified by non-friends and non-members of a certain class. This article was meant to teach you how to do this in the absence of such a keyword.

-Albatross
Feb 22, 2011 at 9:15pm
I think he wants it to be modified but not by anyone outside of the class.
Feb 22, 2011 at 9:16pm
closed account (DEU9GNh0)
Dear jsmith and Framework!

Read this article again!

Readonly to the outside class but read/write within the class, Obviously "const" won't do that!

Maybe you only know ANSI C/C++, but if you know about C++Builder or Delphi there is a syntax to make readonly variables:


1
2
3
4
5
6
7
class A
{
private:
  int _x;
public:
  __property int x = {read=_x};
}



More info:
The real problem is: We can not assign one object of this class to another object because of existence of reference variable.
And the solution is: Writing a copy-constructor and Operator overloading for assignment
Last edited on Feb 23, 2011 at 1:18pm
Feb 22, 2011 at 9:32pm
I don't see anything wrong with the getter returning by value above...actually, how about returning a const reference there?
Feb 22, 2011 at 9:52pm
closed account (DEU9GNh0)
moorecm, You are right and Setter/Getter is the best way but this is just another approach.

We use getter because we have to use it, because there is not another solution in C++ syntax.

Using setter in many cases is logical because we should aware of changing the value of a variable, but using getter only for returning the value of a variable is not always logical. In fact a variable is a variable not a Function or Method !

Feb 22, 2011 at 11:18pm
Masoud... I read it again. Still, a public getter is probably a better approach for the reasons I gave in my original post.
(C++ Builder / Delphi solutions don't interest me since they are vendor extensions and thus not portable).


The real problem is: We can not assign one object of this class to another object because of existence of reference variable.
And the solution is: Writing a copy-constructor


Careful with terminology. Your solution doesn't solve the problem since copy construction and assignment are two different operations in C++. A copy constructor will not help with assignment. A data member that is a reference makes the object copyable (ie, copy constructible) but not assignable.

Feb 23, 2011 at 12:15pm
What I'm saying is that this feature is not needed, an inline getter that returns a const reference does what you want...
Feb 23, 2011 at 1:20pm
closed account (DEU9GNh0)
jsmith, thnx for second part of your post.
We should overload assignment operator for that.
(i edited it.)
Feb 25, 2011 at 3:21pm
lol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A{
private:
	struct data{
		int member;
	};
public:
	A(): set(new data), get(set){}
	A(const A &b): set( new data(*b.set) ), get(set){}
	~A(){
		delete set;
	}
	A& operator=(const A &b){
		*set = *b.set;
		return *this;
	}
	data * const set; //initialization sequence (error prone)
	const data * const get;
};

//Access
A a;
a.set->member = 42;
std::cout << a.get->member;
Last edited on Feb 25, 2011 at 3:23pm
Feb 25, 2011 at 6:15pm
That's pretty cool.
Feb 25, 2011 at 6:23pm
?
1
2
3
4
5
6
7
8
9
10
11
class A {
    int data_;
public:
    const int & get() const { return data_; }
    void set( const int & data ) { data_ = data; }
};

//...
A a;
a.set( 42 );
std::cout << a.get();
Feb 25, 2011 at 6:26pm
The point of having the struct was so you didn't have write a get/set method for every single variable.
Feb 25, 2011 at 7:03pm
I want to ask something. Why is Masoud's class not assignable? I understand the general rule that adding a (const) reference member makes the class not assignable, but in this particular case this is not a problem, as that particular member doesn't need to be changed during the assignment. The only thing that needs to be changed is the real x, not the (public) fake one.
Last edited on Feb 25, 2011 at 7:16pm
Feb 25, 2011 at 7:17pm
Well the compiler doesn't know that. The pointer could point to anything...
Feb 25, 2011 at 7:21pm
Yes, I know.

What I want to say is that in this case the problem can be solved by providing your own assignment operator.

EDIT: Never mind. I just noticed that the suggestion for an overloaded assignment operator was added later.
Last edited on Mar 1, 2011 at 2:59pm
Mar 1, 2011 at 12:42am
m4sterr0shi, why would I want to have to implement an assignment operator in order to have that proposed solution? It doesn't make things any easier does it?

moorecm, there is no benefit to returning a const reference to an integer unless you need to have a reference. It won't save you any time since a pointer has to be copied. The only reason to use a reference is if you needed to have a variable that would change when the underlying class attribute changes but if you need that then the design might be a problem. Moreover it could end up being very confusing for readers if you were to use that concept throughout a large scale program. You could easily lose track of which variables are truly local and which ones are really just pointers to private class attributes.

Anyway I don't see the lack of the readonly keyword as a weakness. C++ just happens to be a different language then C# and the designer had a different idea about how to do that. It seems like a waste of time to design work arounds to make make C++ look like C#. It's not the same language.
Mar 1, 2011 at 2:44pm
kempofighter wrote:
why would I want to have to implement an assignment operator in order to have that proposed solution? It doesn't make things any easier does it?

No, it doesn't. I don't disagree with that. Well, there is a way to make it work without providing an assignment operator. You could encapsulate the read-only functionality in a separate class and overload the assignment operator of that class to do nothing. Then you can use that class to build classes with read-only variables without having to overload the assignment operator again (the default assignment operator will call the overloaded assignment operator of the read-only class, which does nothing).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
using namespace std;

template <class T>
class ReadOnly
{
private:

    const T & real_data;

public:

    ReadOnly(const T & real_data_):real_data(real_data_){}

    ReadOnly & operator=(const ReadOnly &)
    {
        cout << "inside ReadOnly assignment operator" << endl;
        return *this;
    }

    operator const T &() {return real_data;}
};

class MyClass
{
private:

    int real_x;

public:

    ReadOnly<int> x;

    MyClass():real_x(0),x(real_x) {}
    MyClass(const MyClass & obj):real_x(0),x(real_x) {real_x=obj.real_x;}

    void SetX(int n) {real_x=n;}
};

void print_int(int n) {cout << n << endl;}
void print_double(double d) {cout << d << endl;}
void set(int & n, int m) {n=m;}

int main()
{
    MyClass a;

    //a.x=1; //ERROR!!!
    a.SetX(1);

    MyClass b(a); //Copy Constructor

    b.SetX(2);

    cout << "assigning b to c" << endl;
    MyClass c; c=b; //Assignment

    //set(c.x,3); //ERROR!!!
    c.SetX(c.x+1);

    cout << "a.x: "; cout << a.x << endl;
    cout << "b.x: "; print_int(b.x);
    cout << "c.x: "; print_double(c.x);

    return 0;
}

I wonder if it can be done without writing a (copy) constructor too...

Perhaps by having a ReadOnly variable which also happens to be
the real variable and making the owning class a friend of that variable...

EDIT: Something like this (the testing code is the same as above):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class T>
struct FriendMaker {typedef T Type;};

template <class T, class C>
class ReadOnly
{
friend class FriendMaker<C>::Type;

private:
    T data;
    ReadOnly & operator=(const T & t) {data=t;}

public:
    operator const T &() {return data;}
};

class MyClass
{
public:
    ReadOnly<int,MyClass> x;
    void SetX(int n) {x=n;}
};

kempofighter wrote:
Anyway I don't see the lack of the readonly keyword as a weakness.

Neither do I. But I find it challenging/entertaining to come up with such hacks/work-arounds.
At the very least, it can help one understand better how C++ works.
Last edited on Mar 1, 2011 at 3:51pm
Topic archived. No new replies allowed.