简体   繁体   中英

Are C++ nested classes the right approach for encapsulation?

I have a global class TRK that has many members and methods. I wanted to organize these by sorting them into different named categories, eg Fitting , such that the namespace of the main class isn't overcrowded. In a perfect world, I would want this example to work:

class TRK
{
    public:
        // constructors
        TRK(...);
        ...
        TRK();
        ~TRK();


        // nested classes
        class Fitting;

        // and many other methods...


    private:
        ...
};

class TRK::Fitting
{
    public:
        // constructors/destructor
        TRK& trk;
        Fitting();
        ~Fitting();

        // and many other methods...


    private:
        ...

};

The key thing here that I need is to be able to:

  1. Instance some TRK object using one of the TRK class' constructors, and I need the TRK constructors to be able to also automatically instantiate accompanying nested classes eg Fitting , for that instance of TRK . I then need to be able to instantiate/give values to members of these nested classes, within the TRK constructors. For example, if Fitting has some member x , I need to be able to initialize the value for x for that instance of TRK within the TRK constructor, given the arguments to the constructor. What I'm unclear on is how exactly to go about this; how and where can I instantiate these nested classes?

  2. Access members of the nested classes from TRK instances and methods, and vice versa. I already can do the latter by passing TRK by reference to the nest classes, as shown, but I'm not sure how to do the former.

For example, I have methods of Fitting that need to use members of whatever TRK instance that that instance of Fitting was created within. Similarly, I have methods of Fitting that methods of TRK need to be able to call.

Should I even be using nested classes for this? I tried using namespaces and inheritance but I couldn't get things to work the way that I wanted. My core issue here is attempting

Constructing instances of nested classes

If you want the constructor of TRK to in turn cause the construction of a TRK::Fitting variable, the definition of TRK::Fitting must be completely known to it, a forward declaration is not enough. However, once you do that, you can intialize member variables of the nested class type just like you would always do. Here is an example:

class TRK {
    class Fitting {
        int x;
    public:
        Fitting(int x): x(x) {}
    };

    Fitting fitting;

public:
    TRK(int y): fitting(y) {}
};

Having nested classes access the parent class

A nested class is just a regular class, only its name is nested. It does not automatically know where the non-static member variables of the parent are. A simple solution is to provide the nested class with a reference to the instance of the parent class, like so:

class TRK {
    class Fitting {
        TRK &parent;
        int x;
    public:
        Fitting(TRK &parent, int x): parent(parent), x(x) {}

        void foo() {
            // Use something from the parent class
            parent.bar();
        }
    };

    Fitting fitting;

public:
    TRK(int y): fitting(*this, y) {}
    void bar() {}
};

Another option is to not store a reference to the parent in the child class, but rather to explicitly pass a reference to the parent to every member function of the child class:

class TRK {
    class Fitting {
        void foo(TRK &parent) {
            // Use something from the parent class
            parent.bar();
        }
    };

    Fitting fitting;

public:
    TRK(int y): fitting(y) {}
    void bar() {}
    void quux() {
        fitting.bar(*this);
    }
};

Calling a member function of the child class from the parent class is easy, as shown in TRK::quux() .

If you want to use inheritance and have the base class be able to call functions in the derived class, then the curiously recurring template pattern can be used, like so:

template <typename Derived>
class TRK {
    ...

    void bar() {}

    void quux() {
        // We need to static_cast<> ourself to get an object of type Derived
        static_cast<Derived>(*this)::foo();
    }
};

class Derived: TRK<Derived> {
    ...
    void foo() {
        // We can directly call any base class member functions here
        bar();
    }
}

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