开发者

Name for a public const and private writable attribute?

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开发者_StackOverflow社区 : _count( 0 ), count( _count ){}

     void inc( void ){ _counter++; }
};

Is there a common name for this trick?


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:

Tieing

Typical example:

 tuple<int> tie(ref(some_var));
 // or shorter:
 auto tied = tie(var1, var2, var3);

Assignment complications

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

  • class layout + size
  • copy/assignment semantics
  • compiler optimizations: the compiler will shun from making assumptions about the value of (register-allocated) variables when it knows references could point to the same memory location.

In addition to the points David makes, the compiler will be unable to generate default

  • semantically valid copy constructor
  • assignment operator
  • move assignment operator

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜