15

Take the following header file example, where Bar is a struct:

class Foo
{
    ...
private:
    Bar _bar;
};

I only want Bar to be accessible as a private member variable for Foo. What is the proper way to declare and define Bar?

Option 1: define in header?

I'd like to avoid this, as I don't want Bar to be usable outside the Foo class scope.

struct Bar
{
    int a;
    int b;
    ...
};

class Foo
{
    ...
private:
    Bar _bar;
};

Option 2: forward declare in header, define in cpp?

Not sure if this is legal, as how will the compiler know the size of Foo strictly from the header if Bar's definition isn't directly available? Also, will this hide Bar from other files that include the header?

header:

struct Bar;

class Foo
{
    ...
private:
    Bar _bar;
};

implementation file:

struct Bar
{
    int a;
    int b;
    ...
};

Option 3: declare within class

Maybe the best option to limit scope, but potentially messy?

class Foo
{
    ...
private:
    struct Bar
    {
        int a;
        int b;
        ...
    };

    Bar _bar;
};
schumacher574
  • 1,042
  • 1
  • 13
  • 28

3 Answers3

10

For the option 2 to compile, _bar should be a pointer. Option 3 is best, since it doesn't pollute namespace.

SergeyA
  • 56,524
  • 5
  • 61
  • 116
5

Option 3: declare within class

Maybe the best option to limit scope

Of course, that's the best way to limit scope and doesn't pollute the namespace. Go for option 3.

but potentially messy?

I didn't get your concerns (you may wanted to elaborate that in your question), there's nothing messy with that at all.

Note: Whenever needed to make a roundtrip from clients with it, they can use the auto keyword to refer to Bar variables.


Another option as mentioned by R.Sahu, is to use the Pimpl Idiom:

struct FooData;
class Foo {
    ...
private:

    std::unique_ptr<FooData> data_; // Note I not prefer to use leading underscores

public:
    Foo();
};

And in Foo's translation unit:

namespace {
    struct FooData;
    {
        int a;
        int b;
        ...
    };
}

Foo::Foo() : data_(std::make_unique<FooData<()) {}
Community
  • 1
  • 1
πάντα ῥεῖ
  • 83,259
  • 13
  • 96
  • 175
  • 1
    I edited the question to strike the messy portion out. I was concerned about cluttering the header file with large struct definitions, but I don't think those concerns are really that valid. – schumacher574 Aug 24 '16 at 17:09
  • @schumacher574 I edited my answer to show you a 4. option (Cleanest may be, regarding of your concerns _cluttering the header file ..._) – πάντα ῥεῖ Aug 24 '16 at 18:02
  • @SergeyA I've been thinking about that option, but decided to leave that detail out for clarity. THX for mentioning though. I changed my example to be more compliant what the standards offer. – πάντα ῥεῖ Aug 24 '16 at 18:22
  • @πάντα ῥεῖ unfortunately not all projects that will implement this code have the luxury of a heap, so I can't go with Pimpl. I can see the usefulness of the idiom when available, however. Thanks! – schumacher574 Aug 24 '16 at 18:26
  • @schumacher574 _"so I can't go with Pimpl"_ You don't necessarily need a _heap_ (whatever this means) to realize _Pimpl_. Lookup _placement `new()`_ for instance. – πάντα ῥεῖ Aug 24 '16 at 18:28
  • @πάνταῥεῖ Interesting. So I could allocate on the stack and use a placement new to use the allocated space. Good tool to know. – schumacher574 Aug 24 '16 at 18:34
  • @schumacher574 TBH I can't see why you accepted _@Sergey_'s answer actually? But well ... – πάντα ῥεῖ Aug 24 '16 at 18:35
  • @schumacher574 To have it residing on _the stack_ (whatever this means), you won't want a member variable of `FooData`. So you could use it from a internal anonymous namespace completely. As mentioned from the very 1st time you should elaborate more on your question, what your requirements and concerns are. You're completely drifting off now. – πάντα ῥεῖ Aug 24 '16 at 18:38
  • @πάνταῥεῖ simply because at the time of accepting, he was the first to answer and both of yours seemed to make the same statement. Since then you've edited to include the pimpl option, which is nice. – schumacher574 Aug 24 '16 at 18:40
  • @πάνταῥεῖ Why would I bring up requirements for heap/stack/bss size in my original question when I wasn't even aware that they would come into play? And they aren't a requirement for every situation. I'm simply asking you more information about the pimpl idiom you outlined, and how to implement it without declaring objects on the heap. – schumacher574 Aug 24 '16 at 18:47
  • 1
    @schumacher574 I didn't knew about your memory requirements as well, when you were asking :P. For having everything instantiated on the _"stack"_ just go for option 3 as mentioned initially. – πάντα ῥεῖ Aug 24 '16 at 18:52
4

Option 1: define in header?

I'd like to avoid this, as I don't want Bar to be usable outside the Foo class scope.

With option one, you just answered your own question.

Option 2: forward declare in header, define in cpp

Same problem here as option 1(scope visibility ), but only advantage is Bar's implementation won't be visible to other .cpp files.

Option 3: declare within class

Option 3 is the best, since it fulfills the purpose you ask for, and only that. In addition, with that Bar is available to the whole class. Also, nesting the class prevents unnecessary clutter in your code, as nothing except Foo can access Bar. And it definitely does NOT seem messy, and plus, you could declare just the struct like this:

class Foo
{
private:
    struct Bar {int a, int b};
    //.....
};

For a small class, and that seems ok since it is a 1-liner. A disadvantage would be forward-declaring the class, not possible, as seen in this answer: https://stackoverflow.com/a/951245/6525260.

Community
  • 1
  • 1
Arnav Borborah
  • 9,956
  • 4
  • 32
  • 69