Const reference field as readonly property in C++ class
Asked Answered
C

4

16

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';
Cypress answered 18/9, 2017 at 8:58 Comment(5)
And can be const cast to oblivion :)Lamed
No, it is a bad practice since storing this reference may cause an extra memory overhead compared to a straightforward inline getter function. Another thing is that you won't be able to perform runtime checks / asserts regardless the value being accessed. Unfortunately there is no real way to emulate C# properties -like syntax. There was a MS-specific property extension but they are non-standard and were abandoned.Fifty
Try to assign one check to another.Isis
IMO no, it's annoying and mucks around with value semantics and lifetimes. E.g. if your object is moved or copied then the reference still refers to the old object's, which may be destroyedTowage
Important to note, idioms in a language is not in another, trying to use c++ as another language is unlikely to work wellEdgell
F
6

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.
Flap answered 18/9, 2017 at 9:23 Comment(0)
W
19

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++?

Weariful answered 18/9, 2017 at 9:5 Comment(2)
Try assigning one check in your code to another and see what happens. Your object is copied No, it isn't. The assignment is ill-formed because the class is non-assignable.Destruction
The copying issue only means that you have to supply copy and move constructors that ensure that the reference points into the local object. Assignment is not a problem, as it's impossible to change a reference in an assignment operator anyway, and a destructor is also not necessary.Paramagnet
F
6

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.
Flap answered 18/9, 2017 at 9:23 Comment(0)
W
4

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.

Wartime answered 18/9, 2017 at 10:11 Comment(0)
I
1

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.

Intimidate answered 18/9, 2017 at 15:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.