87

Whoopee, not working on that socket library for the moment. I'm trying to educate myself a little more in C++.

With classes, is there a way to make a variable read-only to the public, but read+write when accessed privately? e.g. something like this:

class myClass {
    private:
    int x; // this could be any type, hypothetically

    public:
    void f() {
        x = 10; // this is OK
    }
}

int main() {
    myClass temp;

    // I want this, but with private: it's not allowed
    cout << temp.x << endl;


    // this is what I want:

    // this to be allowed
    temp.f(); // this sets x...

    // this to be allowed
    int myint = temp.x;

    // this NOT to be allowed
    temp.x = myint;
}

My question, condensed, is how to allow full access to x from within f() but read-only access from anywhere else, i.e. int newint = temp.x; allowed, but temp.x = 5; not allowed? like a const variable, but writable from f()...

EDIT: I forgot to mention that I plan to be returning a large vector instance, using a getX() function would only make a copy of that and it isn't really optimal. I could return a pointer to it, but that's bad practice iirc.

P.S.: Where would I post if I just want to basically show my knowledge of pointers and ask if it's complete or not? Thanks!

FurryHead
  • 1,479
  • 3
  • 16
  • 19

13 Answers13

75

Of course you can:

class MyClass
{
    int x_;

public:
    int x() const { return x_; }
};

If you don't want to make a copy (for integers, there is no overhead), do the following:

class MyClass
{
    std::vector<double> v_;

public:
    decltype(v)& v() const { return v_; }
};

or with C++98:

class MyClass
{
    std::vector<double> v_;

public:
    const std::vector<double>& v() const { return v_; }
};

This does not make any copy. It returns a reference to const.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • OK, I think this is what I want. I would use like: `MyClass mclass; vector *temp = mclass.x()`? – FurryHead Mar 24 '11 at 19:04
  • @FurryHead: nope, you would use it like `const vector& tmp = mclass.x()` and use `tmp` as if it were a non modifiable vector (the *compiler* won't let you modify it). You can do also `double x = mclass.x()[3]` but not `mclass.x()[3] = 42`. – Alexandre C. Mar 24 '11 at 19:07
  • `MyClass mclass; const vector& v = mclass.x();` – juanchopanza Mar 24 '11 at 19:08
  • 1
    @Alexandre, Great example. One last question about your solution: If mclass gets destructed, would the reference-to-x tmp have undefined contents/behaviour ? – FurryHead Mar 24 '11 at 19:14
  • 1
    @FurryHead: Good point: yes, accessing the referenced object is undefined behaviour in the case it has been destroyed. This situation is known as a "dangling reference". In this respect, references are very similar to pointers. – Alexandre C. Mar 24 '11 at 19:29
  • Why use the `std::vector` rather than `const int& x() { return x_; } const`? *(I added the `const` at the end.)* – Thomas Matthews Mar 24 '11 at 20:10
  • @Thomas - Why a reference to an int, when you can return the int directly. To use it, you must copy the value anyway. The vector is different, as might not use all the values. – Bo Persson Mar 24 '11 at 21:27
  • @Thomas: there is no overhead in returning the int by value (but there may be overhead in accessing an integer by reference), and it is simpler to do so. – Alexandre C. Mar 24 '11 at 21:50
  • 1
    I still don't see the advantage of returning a `std::vector` with only one element. My question still applies to POD types. A `std::vector` requires copying the return value into the vector; or at some point the value must be copied into the vector: an additional copy instruction. Returning the item alone is one copy operation. I'm confused. – Thomas Matthews Mar 24 '11 at 22:52
  • 2
    @ThomasMatthews Alexandre is just giving two different examples. If u have an int dont return a const ref, if u have a std::vector return a const ref. We don't mean to store the single int from the first example in a vector. – Moberg Mar 25 '14 at 09:02
  • I spent a long time being confused by this, but now I think finally get it. [Here](http://www.tutorialspoint.com/compile_cpp11_online.php?PID=0Bw_CjBb95KQMQnJKNlQ1cTZtb3M) is a snippet I've just made which others may find helpful. – dan-man Dec 16 '14 at 00:52
  • should be corrected to: **const** decltype(v_)& v() const { return v_; } – starriet Sep 05 '22 at 00:26
64

While I think a getter function that returns const T& is the better solution, you can have almost precisely the syntax you asked for:

class myClass {
    private:
    int x_; // Note: different name than public, read-only interface

    public:
    void f() {
        x_ = 10; // Note use of private var
    }
    const int& x;
    myClass() : x_(42), x(x_) {} // must have constructor to initialize reference
};

int main() {
    myClass temp;

    // temp.x is const, so ...
    cout << temp.x << endl; // works
    // temp.x = 57;  // fails

}

EDIT: With a proxy class, you can get precisely the syntax you asked for:

class myClass {
public:

    template <class T>
    class proxy {
        friend class myClass;
    private:
        T data;
        T operator=(const T& arg) { data = arg; return data; }
    public:
        operator const T&() const { return data; }
    };

    proxy<int> x;
    // proxy<std::vector<double> > y;


    public:
    void f() {
        x = 10; // Note use of private var
    }
};

temp.x appears to be a read-write int in the class, but a read-only int in main.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Why not. I upvote this because this tries hard to get the desired behaviour. However, if you have eg. `void f(MyClass)` and `MyClass` is contructible by an `int`, you can't do `f(myObj.x)`, so the behaviour is not quite the same. – Alexandre C. Mar 24 '11 at 21:54
  • 1
    @AlexandreC.: I think with C++11 you can set the constructor to explicit, like so: `explicit MyClass(int s);`, which will prevent the implicit creation of a temporary MyClass. – Markus Jun 03 '14 at 13:10
  • @Markus: Actually my point was the exact opposite: you cannot chain user-defined conversions, you are allowed at most one. Since you already spent the conversion `proxy -> int`, you cannot use `myObj.x` as an argument of a function expecting something implicitly constructible from int. (also C++03 allows `explicit` as well) – Alexandre C. Jun 03 '14 at 19:40
  • @AlexandreC. it isn't the same, but I can't think of a use case right now where I would need the sort of functionality you're talking about. What's a case where that would be particularly useful? I can only think of cases involving generated code where you can't modify the generator, or don't want to. – Merlyn Morgan-Graham Oct 03 '16 at 07:19
  • 1
    thank you man, this is what i want: simple and clear solution. – Rinat Dec 10 '16 at 17:13
  • Great tweak. What's the difference between the two examples? Does using a proxy class have any advantage over the first example? (P.S. FYI for readers: If you want, you can use `const int& x = x_;` to initialize the reference `x`, instead of letting the constructor initialize it, in the first example.) – starriet Sep 03 '22 at 06:57
43

A simple solution, like Rob's, but without constructor:

class myClass {
private:
    int m_x = 10; // Note: name modified from read-only reference in public interface
public:
    const int& x = m_x;
};

int main() {
    myClass temp;

    cout << temp.x << endl; //works.
    //temp.x = 57;  //fails.
}

It is more like get method, but shorter.

Constant pointer is simple, and should work at all types you can make pointer to.

greybeard
  • 2,249
  • 8
  • 30
  • 66
6

This may do what you want.

If you want a readonly variable but don't want the client to have to change the way they access it, try this templated class:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    inline operator primative() const                 { return x; }

    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x <<y; }
    template<typename number> inline number operator>>(const number& y) const { return x >> y; }
    template<typename number> inline number operator^ (const number& y) const { return x ^ y; }
    template<typename number> inline number operator| (const number& y) const { return x | y; }
    template<typename number> inline number operator& (const number& y) const { return x & y; }
    template<typename number> inline number operator&&(const number& y) const { return x &&y; }
    template<typename number> inline number operator||(const number& y) const { return x ||y; }
    template<typename number> inline number operator~() const                 { return ~x; }

protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    template<typename number> inline number operator&=(const number& y) { return x &= y; }
    template<typename number> inline number operator|=(const number& y) { return x |= y; }
    primative x;                                                                                
};      

Example Use:

class Foo {
public:
    ReadOnly<Foo, int> x;
};

Now you can access Foo.x, but you can't change Foo.x! Remember you'll need to add bitwise and unary operators as well! This is just an example to get you started

Jonathan
  • 6,741
  • 7
  • 52
  • 69
  • Any thoughts on how to extend this to arbitrary classes, not just primitives? Preferably avoiding the container class having to use special syntax for read and write access. – Alec Jacobson Oct 14 '14 at 23:48
  • Also, this unfortunately doesn't play well with derived classes. Suppose you have `class Bar : public Foo`, you can't seem to have Foo's members publicly _read only_ but _read and write_ accessible to member functions in `Bar` (non-inheritance and non-transitivity of friendship). – Alec Jacobson Oct 15 '14 at 03:06
  • This is designed for primitives. Class members that are objects can have const functions to make them 'read only'. To make them accessible by the containing class (as the question pertains) make that class a friend class and make the non-const functions private. – Jonathan Oct 17 '14 at 20:54
  • But I'll need to make the derived class a friend of `ReadOnly`, but if the inherited class has `ReadOnly` members and doesn't know about its derived classes yet, there's no way to know to make the derived classes friends of `ReadOnly`. So then the `ReadOnly` members of the inherited class are not "writable" by the derived class. See new question http://stackoverflow.com/q/26383155/148668 – Alec Jacobson Oct 19 '14 at 18:54
  • this can be solved by using a ganeric interface, as long the derived uses that interface than u r good – LemonCool Nov 09 '15 at 21:42
  • How about `operator--` and `operator++`? – Jenia Dec 22 '20 at 10:45
3

There is a way to do it with a member variable, but it is probably not the advisable way of doing it.

Have a private member that is writable, and a const reference public member variable that aliases a member of its own class.

class Foo
{
  private:
      Bar private_bar;

  public:
      const Bar& readonly_bar; // must appear after private_bar
                              // in the class definition

  Foo() :
       readonly_bar( private_bar )
  {
  }
};

That will give you what you want.

void Foo::someNonConstmethod()
{
    private_bar.modifyTo( value );
}

void freeMethod()
{
    readonly_bar.getSomeAttribute();
}

What you can do, and what you should do are different matters. I'm not sure the method I just outlined is popular and would pass many code reviews. It also unnecessarily increases sizeof(Foo) (albeit by a small amount) whereas a simple accessor "getter" would not, and can be inlined, so it won't generate more code either.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • You've misunderstood reference. In this case, the reference won't occupy any extra memory as required by c++. See https://stackoverflow.com/questions/1179937/how-does-a-c-reference-look-memory-wise – attempt0 Jan 17 '22 at 18:49
  • If you do `const Bar& readonly_bar = private_bar`, then it'll be even better as the compiler won't miss the optimization. – attempt0 Jan 17 '22 at 18:58
3

You may want to mimic C# properties for access (depending what you're going for, intended environment, etc.).

class Foo
{
  private:
    int bar;

  public:
    __declspec( property( get = Getter ) ) int Bar;

    void Getter() const
    {
      return bar;
    }
}
Brad Christie
  • 100,477
  • 16
  • 156
  • 200
2

You would have to leave it private and then make a function to access the value;

private:

    int x;

public:

    int X()
    {
        return x;
    }
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
NeilPearson
  • 128
  • 4
2

As mentioned in other answers, you can create read only functionality for a class member by making it private and defining a getter function but no setter. But that's a lot of work to do for every class member.

You can also use macros to generate getter functions automatically:

#define get_trick(...) get_
#define readonly(type, name) \
private: type name; \
public: type get_trick()name() {\
    return name;\
}

Then you can make the class this way:

class myClass {
    readonly(int, x)
}

which expands to

class myClass {
    private: int x;
    public: int get_x() {
        return x;
    }
}
1

Write a public getter function.

int getX(){ return x; }
hfitzwater
  • 544
  • 3
  • 11
1

You need to make the member private and provide a public getter method.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • As I just commented on appleskin's answer, and edited my post, that wouldn't be optimal as that makes a copy of the variable to return, and I plan to be returning a large vector. – FurryHead Mar 24 '11 at 19:01
  • @FurryHead: Then you should of course return by const reference. Anyway, that information was not available by the time I answered. – Jon Mar 24 '11 at 20:27
1

The only way I know of granting read-only access to private data members in a c++ class is to have a public function. In your case, it will like:

int getx() const { return x; }

or

int x() const { return x; }.

By making a data member private you are by default making it invisible (a.k.a no access) to the scope outside of the class. In essence, the members of the class have read/write access to the private data member (assuming you are not specifying it to be const). friends of the class get access to the private data members.

Refer here and/or any good C++ book on access specifiers.

Community
  • 1
  • 1
yasouser
  • 5,113
  • 2
  • 27
  • 41
  • 1
    Thanks for the reference. I was only making example using private:, I don't want private as it blocks all access. I want it to act like a const publicly, but a non-const locally. – FurryHead Mar 24 '11 at 19:07
0

I had a similiar problem. Here is my solution:

enum access_type{readonly, read_and_write};

template<access_type access>
class software_parameter;

template<>
class software_parameter<read_and_write>{
protected:
    static unsigned int test_data;
};

template<>
class software_parameter<readonly> : public software_parameter<read_and_write>{
public:
    static const unsigned int & test_data;
};

class read_and_write_access_manager : public software_parameter<read_and_write>{
    friend class example_class_with_write_permission;
};

class example_class_with_write_permission{
public:
    void set_value(unsigned int value);
};

And the .cpp file:

unsigned int software_parameter<read_and_write>::test_data=1;
const unsigned int & software_parameter<readonly>::test_data=software_parameter<read_and_write>::test_data;

void example_class_with_write_permission::set_value(unsigned int value){software_parameter<read_and_write>::test_data=value;};

The idea is, that everyone can read the software parameter, but only the friends of the class read_and_write_access_manager are allowed to change software parameter.

Markus
  • 1
  • 1
-1

but temp.x = 5; not allowed?

This is any how not allowed in the snippet posted because it is anyhow declared as private and can be accessed in the class scope only.

Here are asking for accessing

cout << temp.x << endl;

but here not for-

int myint = temp.x;

This sounds very contradictory.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • I know. But a private: variable cannot be accessed with `int myint = temp.x;` - I want read access, but not write access to the public. – FurryHead Mar 24 '11 at 19:00
  • If it's a `private` variable, it can be accessed in the scope of class only.(period) – Mahesh Mar 24 '11 at 19:03
  • 1
    I was only making example using private:, I don't want private as it blocks all access. I want it to act like a const publicly, but a non-const locally. – FurryHead Mar 24 '11 at 19:08