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
- the size of the struct or class in use and
- 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 ofstd::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).