简体   繁体   中英

C++: access const member vars through class or an instance?

In C++, is there any reason to not access static member variables through a class instance? I know Java frowns on this and was wondering if it matters in C++. Example:

class Foo {
  static const int ZERO = 0;
  static const int ONE = 1;
  ...
};


void bar(const Foo& inst) {
   // is this ok?
   int val1 = inst.ZERO;
   // or should I prefer:
   int val2 = Foo::ZERO
   ...
};

I have a bonus second question. If I declare a static double, I have to define it somewhere and that definition has to repeat the type. Why does the type have to be repeated? For example:

In a header:
  class Foo {
    static const double d;
  };
In a source file:
  const double Foo::d = 42;

Why do I have to repeat the "const double" part in my cpp file?

I would prefer Foo::ZERO over inst.ZERO because it more clearly tells what is going on. However, in a method of class Foo , I would just use ZERO .

As for the bonus question, const just is part of the complete type.

For the first question, aside from the matter of style (it makes it obvious it's a class variable and has no associated object), Fred Larsen, in comments to the question, makes reference to previous question. Read Adam Rosenthal's answer for very good reason why you want to be careful with this. (I'd up-vote Fred if he'd posted it as answer, but I can't so credit where it's due. I did up-vote Adam.)

As to your second question:

Why do I have to repeat the "const double" part in my cpp file?

You have to repeat the type primarily as an implementation detail: it's how the C++ compiler parses a declaration. This isn't strictly ideal for local variables either, and C++1x (formerly C++0x) makes use of the auto keyword to avoid needing to be repetitive for regular function variables.

So this:

vector<string> v;
vector<string>::iterator it = v.begin();

can become this:

vector<string> v;
auto it = v.begin();

There's no clear reason why this couldn't work with static as well, so in your case thos:

const double Foo::d = 42;

could well become this.

static Foo::d = 42;

The key is to have some way of identifying this as a declaration.

Note I say no clear reason: C++'s grammar is a living legend: it is extremely hard to cover all of its edge cases. I don't think the above is ambiguous but it might be. If it isn't they could add that to the language. Tell them about it ... for C++2x :/.

Given that you are declaring them as static and make them class constants, I would use Foo::Zero to communicate the intent to the casual and not so casual reader of your code.

I would also replace the all uppercase names of the constants, ie turn Foo::ZERO into Foo::Zero. Normal convention for preprocessor macros is to name them in all uppercase and using a similar naming scheme for your C++ constants is asking for trouble because the preprocessor might just blat over your C++ constants and you end up with very interesting error messages.

I would use Foo::ZERO, but that is just me. Especially if you derive from Foo, which gets confusing.

For your second question, you need to create memory for the double value, and that happens in an implementation unit.

I think the only type you don't need to create memory for is a const integral type. You can then put the value in the header file. But since it doesn't have an address, you wouldn't be able to pass it by reference to a function, unless you put a definition in a .cpp file. (This seems to be the way it works with gcc).

It doesn't matter which form you use in your example. They both mean the same thing. I'd prefer to use the class way in general because you may not always have an instance handy to use the dot operator on.

It is nice to have both options if you consider someone writing a template function. They may have coded the function with the dot operator. Your class with the static class member could still be used to instantiate the template because that syntax is supported.

As for your bonus question, that's just the way the language is. You always have to declare the full type. You should probably ask that in a separate question.

personally i'd use an anonymous enum. End result is exactly the same though :)

As for your questions. I'd definitely prefer the Foo::Zero because its obvious from looking at it what you are accessing. inst.Zero requires you to work out what type inst is before hand.

You have to repeat the datatype because thats how C++ works. In the same way if you wrote the following in a header file.

extern int foo;

You will still need to mention the

int foo

in a CPP file. As pukku mentioned you are declaring a variable of type "const int". Thus the "const int" must be repeated in the definition of the variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM