Jimbo
Jimbo

Reputation: 4515

C++ nested classes

I have searched through some SO articles but haven't found anything (yet) that quite addresses my question. Apologies if this answer does already exist somewhere.

A bit of background first...

I want to represent a device with "sections" of functionality, where the functionality has a hierarchical tree-like structure. Rather than have a load of flattened functions like

DeviceReferenceCheck(), 
DeviceRefereceSet(), 
DevicePhaseSetX(), 
DevicePhaseDefaultsSet...()

I'd instead like to leverage nested classes so I could get

dev.reference.check()
dev.reference.set()
dev.phase.setx()
dev.phase.defaults.set...()

To do this I'm trying to use nested classes to get the obj.func.subfunction.subsub....() structure. The nested classes need a reference to the outermost class because they need to use read/write functions provided there.

In my attempts, the first thing I don't understand very well is as follows... I had tried this myself but then stopped using it because of a compiler warning.

class GPIBDevice_Agilent53132A : public GPIBDevice
{
private:
    class RefOsc {
    public:
        // ... snip ...
        RefOsc(GPIBDevice_Agilent53132A &parent);
        // ... snip ...
    } ro;
public:
   // ... snip ...
   GPIBDevice_Agilent53132A();
   // ... snip ...
};

GPIBDevice_Agilent53132A::GPIBDevice_Agilent53132A() : GPIBDevice(), ro(*this)
{
}

Compiler says: gpibdevice_agilent53132a.cpp(5): warning C4355: 'this' : used in base member initializer list.

Aha, I think to myself... clever compiler... using this in the initialiser list is probably not a good idea because the class hasn't been fully constructed yet.

Question 1: Is what I've said above correct? Is using this, in the enclosing class' initialiser list, to give the nested class a reference to the enclosing class, a bad idea? My thoughts are "yes" but would like some clarification because in other SO threads I have seen this method being used (Nested Class member function can't access function of enclosing class. Why?).

My approach to get around this was to have a member pointer to nested and then when actually in the constructor (so now safe to use this as class has been constructed) made a new inner class where I could pass in the reference to *this without warnings. Is this the standard way of doing it?

Continuing on....

The reason for the private nested class is btw that I don't want the user to be able to instantiate that class him/herself. Now, I did have it public to begin with... tried to use a private constructor in the nested class, but then the compiler told me it couldn't construct the class. So presumably the enclosing class can see nested class private data members?

Question 2: Why can't the enclosing class see the nested classes private data members/functions?

My work around for this is to have the nested class declare the enclosing class as a friend. Is this really necessary?

Thanks for you help guys!

Upvotes: 4

Views: 2734

Answers (1)

Jimbo
Jimbo

Reputation: 4515

Summary

Thanks to Jan for his explanation of the curiously recurring template pattern. It's an interesting method to know about.

I've ended up accepting my own answer because I feel it answers the questions directly. The CRTP method is good but doesn't directly answer questions 1 & 2, but does provide a good alternative.

Question 1:

It would seem that this is possible. Thank you to mkirci and R. Martinho Fernandes for confirming my suspicions on why the compiler generated the warning and whether it was "safe" to ignore it.

In summary... not the best idea to use this in the initialiser list of the constructor because the class has not yet been constructed. As the guys point out, this can cause UB if the pointer is used. I've decided to use my work around which is use a pointer to the inner class, and then create it once inside the outer-class constructor... this way outer class is already created and can pass a reference to itself to the inner class safely.

Question 2:

From the C++ standard, I have found (after a lot of digging) in section 11.7:

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11) shall be obeyed.

The standard gave the following example:

class E {
    int x;
    class B { };
    class I {
        B b; // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i; // OK: E::I can access E::x
        }
    };

    int g(I* p) {
        return p->y; // error: I::y is private
    }
};

So, this is (annoyingly) why my outer class cannot call the inner class' private constructor. My solution has been to make the outer class a friend of the inner class. I.e in the above example add friend E; into the inner class decl.

Upvotes: 3

Related Questions