Home > Back-end >  How can I create objects of a nested class inside the parent class but in another header file?
How can I create objects of a nested class inside the parent class but in another header file?

Time:06-28

I have a class Dmx with a nested class touchSlider. So before I had these classes in my main.cpp file and just created an object array of touchSlider within the Dmx class and it worked properly.

How can I implement this here, with different header files? The compiler gives an error message: invalid use of incomplete type 'class Dmx::touchSlider'

The object array is: touchSlider slider[10] = {50,130,210,290,370,50,130,210,290,370};

dmx.h

// dmx.h

#ifndef dmx_h
#define dmx_h

class Dmx {
  public:
    byte number;                                                

    Dmx(byte numberA) {                                                 
      number = numberA;
    }

    void settingsDisplay();
    
    class touchSlider;             // declaration of nested class

    touchSlider slider[10] = {50,130,210,290,370,50,130,210,290,370};  
};

#endif

touchSlider.h

// touchSlider.h

#ifndef touchSlider_h
#define touchSlider_h

#include "dmx.h"

class Dmx::touchSlider{
  private:
  int pos;                                     

  public:
  touchSlider(int posA){                        
    pos = posA;                      
  }

  void printChannel();

};

#endif

main.cpp

// main.cpp

#include "dmx.h"                
#include "touchSlider.h"

Dmx dmx[10] = {Dmx(1), Dmx(2),Dmx(3), Dmx(4), Dmx(5), Dmx(6), Dmx(7), Dmx(8), Dmx(9), Dmx(10)}; 


void Dmx::settingsDisplay() {
    // do something
}

void Dmx::touchSlider::printChannel() {
    // do something
}

My previous code (that worked great) where both classes where in the same file looked like this:

class Dmx {
  public:
    byte number;                                                

    Dmx(byte numberA) {                                                 
      number = numberA;
    }

    void channelDisplay(){
    }                                            

    void settingsDisplay(){
    }
    
    class touchSlider{
    private:
      int pos;                                     

    public:
      touchSlider(int posA){                        
         pos = posA;                      
      }

      void setChannel(/* some arguments*/){
      }

      void printChannel();
      }
   };             

    touchSlider slider[10] = {50,130,210,290,370,50,130,210,290,370};        
};
Dmx dmx[10] = {Dmx(1), Dmx(2),Dmx(3), Dmx(4), Dmx(5), Dmx(6), Dmx(7), Dmx(8), Dmx(9), Dmx(10)}; 

CodePudding user response:

In order to create an array of touchSlider the compiler needs a definition of the touchSlider class. So as written this will not work.

In the code given

touchSlider slider[10] = {50,130,210,290,370,50,130,210,290,370};

the complier needs to know how big a touchSlider object is so it can allocate enough memory for a Dmx object. It also needs to know that a touchSlider can be constructed from an int. Both these things require the full definition of touchSlider.

Now maybe you can use some variation to achieve whatever your goal is, but without more details it's hard to suggest anything.

CodePudding user response:

To be able to create an array:

touchSlider slider[10] = {50,130,210,290,370,50,130,210,290,370};

You need the class definition available, because the compiler needs to know

  1. the size of the struct or class in use and
  2. if there's a suitable constructor available.

You now have two options, either you provide the class definition in the header but implement the class within the source file like:

// header:

class Dmx
{
public:
    // ...

    class TouchSlider
    {
    public:
        // only DECLARE here:
        TouchSlider(int posA);
        void setChannel(/* some arguments*/);
        void printChannel();
    };
};

// source:

Dmx::TouchSlider::TouchSlider(int posA)
    : pos(posA) // note: prefer the initialiser list!
{ }

void Dmx::TouchSlider::setChannel(/* some arguments*/)
{
}

// ...

or you hide away the implementation as you intended, but then you need to allocate the memory dynamically (this is the PImpl idiom) – at best with help of a std::unique_ptr:

class Dmx
{
public:
    // ...
private:
    class TouchSlider; // just declare

    std::unique_ptr<TouchSlider[]> sliders;
};

Important (see cppreference), though:

std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the pImpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr.

I.e. you cannot implement e.g. your class' destructor in the header file either but need to do so in the source file as well – after the nested class' full definition – alike any function that might re-assign another array.

The std::unique_ptr avoids necessity of manual memory management (see rules of three/five), on the other hand the class gets non-copiable (but you can work around by providing your own custom copy constructor and assignment while defaulting the move constructor and assignment).

  • Related