Const reference field as readonly property in C++ class

3.3k views Asked by At

Is it good to use a const reference field as a readonly getter in C++ classes?

I mean, does this code meet good practices?

class check{
private:
    int _x;
public:
    const int& x = _x;
    void setX(int v){
        _x = v;
    }
};

It is working very much like C# properties, IMHO, and very easy and clean in class usage code:

  check s;
  int i;
  std::cin >> i;
  s.setX(i);
  std::cout << s.x << '\n';
  s.setX(7);
  // s.x = i; // Error
  std::cout<<s.x<<'\n';
4

There are 4 answers

0
BiagioF On BEST ANSWER

Generally, it is not a good practice.

imho, and very easy and clean in class usage code.

Why should that be clearer and easier?

  • You introduce another variable member (useless overhead). (Generally, the reference will be implemented as an additional member pointer).
  • It makes the code harder to maintain. You are actually creating dependencies among variable members.
  • It makes problem in the assignment and in the copy operations. How is copy operation supposed to work?

The "classic" approach is sound clearer by me, e.g.:

class Foo {
 public:
  void set_num(int value) noexcept { m_num = value; }
  int get_num() const noexcept { return m_num; }
  void set_string(std::string value) noexcept {
      m_str = std::move(value);
  }
  const std::string& get_string() const noexcept {
      return m_str;
  }
 private:
  int m_num;
  std::string m_str;
};

From a performances point of view, this approach should be preferred.

  • Timing complexity: call get_variable on an inline function does not introduce more overhead than your "reference approach". Moreover, it is highly optimizable by the compiler (because of straightforward of the code).
  • Space complexity: it does not introduce additional data member.
2
gsamaras On

do this code meet good practices?

Not really, since it introduces unnecessary complexity and space overhead.

Moreover, you wouldn't be able to perform runtime checks and/or assertions, regardless of the value being accessed.

Furthermore, what happens with the lifetime and semantics?

Try assigning one check in your code to another and see what happens. The assignment is ill-formed because the class is non-assignable. You should provide a copy and move constructor to take care of the reference, so that it won't refer to the old object's data member.

Better use _x directly and have a straightforward inline getter function.


PS: C#-like properties in native C++?

0
Pharap On

When C# compiles a propery it gets compiled into a getter and a setter function.

Here's some C# code that proves this fact:

using System;

namespace Reflect
{
    class Program
    {
        class X
        {
            public int Prop { get; set; }
        }

        static void Main(string[] args)
        {
            var type = typeof(X);
            foreach (var method in type.GetMethods())
            {
                Console.WriteLine(method.Name);
            }
            Console.ReadKey();
        }
    }
}

Your output should be:

get_Prop
set_Prop
ToString
Equals
GetHashCode
GetType

get_Prop is the function that implements the getter. set_Prop is the function that implements the setter.

So even if what you're doing looks similar, it's not the same at all.

Frankly almost everything you could do to try to emulate 'property syntax' in C++ will fall down in one way or another. Most solutions will either cost you memory or it'll have some limitation that makes it more cumbersome than useful.

Just learn to live with getters and setters. Getters and setters are good practice. They're short, they're simple, they're flexible, they're typically good candidates for inlining, everyone understands what they do et cetera.

0
DodgyCodeException On

What you propose is in general a bad idea:

  • You can't implement the property by doing any processing (e.g. with a getter you can store co-ordinates using [x,y] and later decide to change the implementation to use [angle,radius] while keeping the same public interface).
  • Using a const member variable involves space overhead, and doesn't give you any performance advantage compared with an inline getter.
  • It's not idiomatic.
  • Once you've published your class, and other people start using it, you're stuck with it: it's too late to change it to use a method instead.

If your intention with properties is to make the code more succinct, you don't have to use the words "get" and "set" in your function names; that's an old-fashioned practice. You can use the actual name of the "property" as the function name, and you can overload the getter and setter:

class Cheque {
public:
    int x() const {
        return x_;
    }
    Cheque & x(int newX) {
        x_ = newX;
        return *this;
    }
private:
    int x_;
}

// Usage:
// int amt = myCheque.x(1234);
// Cheque c = Cheque().x(123);

Returning *this as in the above code enables you to use method chaining; a way of implementing the Fluent interface idiom.