Programming in C++, I often want to give the user of a class read-only access to an attribute, and the class itself read-write access. I hate XxxGet()
methods, so I often use a public const &
to a private attribute, like this:
class counter {
private:
int _count;
public:
const int & count;
counter : _count( 0 ), count( _count ){}
void inc( void ){ _counter++; }
};
Is there a common name for this trick?
The const keyword specifies that a variable's value is constant and tells the compiler to prevent the programmer from modifying it.
The const member functions are the functions which are declared as constant in the program. The object called by these functions cannot be modified. It is recommended to use const keyword so that accidental changes to object are avoided.
Integer constant in C is a data type that is represented by const int . const int is capable of storing an integer in decimal, octal, and hexadecimal bases. The value to const int is assigned only when it is declared and cannot be changed afterward.
My personal name for that trick would be bad idea.
I would avoid the approach that you are following, as it incurs extra unneeded cost. If you add accessors they can be inlined as needed, with the only penalty of having to type an extra pair of parentheses:
class counter {
int _count;
public:
counter() : _count() {}
int count() const { return _count; }
void inc() { ++_count; }
};
The main difference is that in your solution you are incrementing the size of the object by one reference (for most implementations this means pointer), and then each access requires an extra indirection. On the other hand, with the accessor, the actual variable is used, the function will be optimized away (inlined, and resolved to a single read to the variable).
As of a proper name for that type of construct, well, I have never seen your particular construct in C++, but if you consider other languages, that is the basic concept of a property in C#, where you can make the getter public and the setter private.
EDIT: I guess that bad idea can be misinterpreted as just a personal opinion (which it is), but consider the side effects of that design:
Because of the reference in the object, you inhibit the implicit definition of the assignment operator. Much worse, the copy constructor will compile but not work as expected:
// consider the implementation with the const reference
counter c1;
counter c2( c1 ); // compiles, so it must work
c2.inc();
std::cout << c2.count; // outputs 0
// c2 = c1; // error: well, at least this does not compile!
The problem is that the compiler generated copy constructor will make the count
reference in c2
refer to the same int
that the count
reference in c1
refers to, which might lead to hard-to-find subtle issues in your code that are actually quite hard to debug.
Edit
Just now I thought of a name that could be considered the same pattern. Though not typically used for member variables.
There could actually be a name for this, as has been made popular by the Boost Tuple library as well as the TR1/C++11 implementations:
Typical example:
tuple<int> tie(ref(some_var));
// or shorter:
auto tied = tie(var1, var2, var3);
The closest name for this (anti?) pattern I could _immediately think of before, is: pointer or reference aliasing. It is not a very good idea for many reasons, some of which have been mentioned
In addition to the points David makes, the compiler will be unable to generate default
for your class now that contains references. Note also that your class can't possibly be POD anymore
A number of others have already condemned this idea, and I (mostly) tend to agree with them. Although quite a few people probably dislike it (at least) as much, if I was going to support something on this order, I'd do something like this:
class counter {
int count_;
public:
counter(int init=0) : count_(init) {}
operator int() const { return count_; }
void inc() { ++count_; }
};
The one problem with this is one that's shared with implicit conversions in general: that the implicit conversion can happen even when you don't want it to. OTOH, the fact that it's a user-supplied conversion actually eliminates many of the problems -- only one implicit conversion will happen automatically in any given situation, so (for example) the fact that you've supplied a conversion to int
will not mean that a counter
with a value of 0 can be implicitly converted from a counter
to an int
to a (null) pointer to T
, because that would involve two implicit conversions.
There are times this can cause a problem anyway, in which case (as of C++11) you can make the conversion operator explicit
, so it'll only happen when/if the user does an explicit conversion like:
counter t;
int x = t; // allowed by code above, but not with `explicit` conversion operator.
int y = static_cast<int>(t); // allowed with `explicit` conversion operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With