Readonly class variables in C++

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
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.

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 ); .
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
I think he wants it to be modified but not by anyone outside of the class.
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
I don't see anything wrong with the getter returning by value above...actually, how about returning a const reference there?
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 !

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.

What I'm saying is that this feature is not needed, an inline getter that returns a const reference does what you want...
closed account (DEU9GNh0)
jsmith, thnx for second part of your post.
We should overload assignment operator for that.
(i edited it.)
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
That's pretty cool.
?
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();
The point of having the struct was so you didn't have write a get/set method for every single variable.
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
Well the compiler doesn't know that. The pointer could point to anything...
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
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.
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
Topic archived. No new replies allowed.