0

I have class called HighWaterDetector:

class HighWaterDetector {
public:
    HighWaterDetector(Device* device);
    NCD2Relay ncd2Relay;
    // Output output1;
    Output outputs[2];
    CloudMsgParser cloudMsgParser;
    Device * devicePtr;
};

How do I initialize the array of "Output" objects in the constructor of HighWaterDetector?

Output class:

 class Output
{
public:
    Output(ushort relayNum, NCD2Relay* ncd2RelayPtr);
    ushort relayNum;
    OutputStatus outputStatus;
    int setOutputOn(void);
    int setOutputOff(void);
    void process(void);
    NCD2Relay* ncd2RelayPtr;
};

with output constructor looking like:

Output::Output(ushort relayNum, NCD2Relay* ncd2RelayPtr2) {
    this->relayNum = relayNum;
    this->ncd2RelayPtr = ncd2RelayPtr2;
}

I am new to C++ and not sure if I can make the HighWaterDetector Constructor look like:

HighWaterDetector::HighWaterDetector(Device* device){
    ncd2Relay = NCD2Relay();
    outputs[0] = Output(1, &ncd2Relay);
    outputs[1] = Output(2, &ncd2Relay);
    cloudMsgParser = CloudMsgParser();

}

Getting compile errors:

highWaterDetector.cpp: In constructor 'HighWaterDetector::HighWaterDetector(Device*)':
highWaterDetector.cpp:8:52: error: no matching function for call to 'Output::Output()'
 HighWaterDetector::HighWaterDetector(Device* device){
                                                    ^
highWaterDetector.cpp:8:52: note: candidates are:
In file included from highWaterDetector.h:10:0,
                 from highWaterDetector.cpp:1:
output.h:20:2: note: Output::Output(ushort, NCD2Relay*)
  Output(ushort relayNum, NCD2Relay* ncd2RelayPtr);
  ^
output.h:20:2: note:   candidate expects 2 arguments, 0 provided
output.h:17:7: note: constexpr Output::Output(const Output&)
 class Output
       ^
output.h:17:7: note:   candidate expects 1 argument, 0 provided
output.h:17:7: note: constexpr Output::Output(Output&&)
output.h:17:7: note:   candidate expects 1 argument, 0 provided
highWaterDetector.cpp: In constructor 'HighWaterDetector::HighWaterDetector(Device*)':
highWaterDetector.cpp:8:52: error: no matching function for call to 'Output::Output()'
 HighWaterDetector::HighWaterDetector(Device* device){
Felix
  • 51
  • 2
  • 10

3 Answers3

1

If you are using C++11 or above, You should write your code like this:

class Output
{
public:
    Output(ushort relayNum, NCD2Relay* ncd2RelayPtr);
    ushort relayNum;
    OutputStatus outputStatus;
    int setOutputOn(void);
    int setOutputOff(void);
    void process(void);
    NCD2Relay* ncd2RelayPtr;
};
class HighWaterDetector {
public:
    HighWaterDetector(Device* device);
    NCD2Relay ncd2Relay;
    // Output output1;
    Output outputs[2];
    CloudMsgParser cloudMsgParser;
    Device * devicePtr;
};

Output::Output(ushort relayNum, NCD2Relay* ncd2RelayPtr2)
    : relayNum(relayNum), ncd2RelayPtr(ncd2RelayPtr2)
{
}
HighWaterDetector::HighWaterDetector(Device* device)
    : ncd2Relay(),
      outputs{Output(1, &ncd2Relay), Output(2, &ncd2Relay)},
      cloudMsgParser(),
      devicePtr(device)
{
}

Live Demo:

Without C++11, you need to create a default constructor

Danh
  • 5,916
  • 7
  • 30
  • 45
  • I thought about that, but wasn't sure if the `outputs{Output(...), Output(...)}` syntax was valid prior to C++11 or not. – Remy Lebeau Aug 11 '16 at 04:43
  • See http://en.cppreference.com/w/cpp/language/aggregate_initialization array type isn't marked `since C++11`, it should be fine with C++03, in my demo, I checked it with both C++03 and C++11 mode (with flag `-std`) – Danh Aug 11 '16 at 04:44
  • That page says: "*Until C++11, aggregate initialization could not be used in a constructor initializer list due to syntax restrictions.*" I have a pre-C++11 compiler where this type of array initialization DOES NOT compile. Maybe it is a gcc extension prior to C++11? – Remy Lebeau Aug 11 '16 at 04:52
0

Firstly, I think you need to have a default constructor for Output.

The default constructor is a constructor that takes no arguments. This is what will get called for each item in the array.

Secondly, please post your constructor for HighWaterDecorator. I suspect you could initialize ncdRelay in the constructor, and pass it to construct the two Output objects in the array.

Aaditya Kalsi
  • 1,039
  • 8
  • 16
  • The HighWaterDetector constructor was posted in my question. Thanks for your help. – Felix Aug 11 '16 at 04:22
  • Ah! I didn't see that. Adding a default constructor for `Output` should be good enough. – Aaditya Kalsi Aug 11 '16 at 04:24
  • How would I initialize Output with default constructor though? Output requires relayNum and ncd2RelayPtr to be passed to its constructor. Any ideas? – Felix Aug 11 '16 at 04:27
0

Your Output class does not have a default (no input) constructor, only a non-trivial constructor that requires input values, so you cannot declare a fixed array of Output elements.

Give Output a default constructor:

class Output
{
public:
    Output(); // <-- here
    Output(ushort relayNum, NCD2Relay* ncd2RelayPtr);
    ...
};

Output::Output() {
    this->relayNum = 0;
    this->ncd2RelayPtr = 0;
}

Alternatively, you can give your existing constructor default parameter values so it can also act as a default constructor:

class Output
{
public:
    Output(ushort relayNum = 0, NCD2Relay* ncd2RelayPtr = 0);
    ...
};

Either way, you can then do this in the HighWaterDetector constructor:

HighWaterDetector::HighWaterDetector(Device* device)
    : devicePtr(device)
{
    outputs[0] = Output(1, &ncd2Relay);
    outputs[1] = Output(2, &ncd2Relay);
}

If you don't like that, then change your outputs[] array into a std::vector instead:

#include <vector>

class HighWaterDetector {
public:
    ...
    std::vector<Output> outputs;
    ...
};

HighWaterDetector::HighWaterDetector(Device* device)
{
    ...
    outputs.reserve(2);
    outputs.push_back(Output(1, &ncd2Relay));
    outputs.push_back(Output(2, &ncd2Relay));
    ...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I'm a little confused. If output has a default constructor, how is it possible you can do "outputs[0] = Output(1, &ncd2Relay);" in your first solution? – Felix Aug 11 '16 at 04:31
  • The `outputs[]` array elements first get default constructed before the `HighWaterDetector` constructor is entered. That is what the compiler error is complaining about - a missing `Output` default constructor. So add one. Then, once the constructor is entered, new `Output` objects are constructed using your 2-input constructor, and then are copy-assigned into the array. The compiler will have generated a default copy constructor and copy assignment operator for you. Or you can define your own if needed (see [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming))). – Remy Lebeau Aug 11 '16 at 04:34
  • Interesting... so Output can have two constructors, a default and another? How does it know to use the default first? – Felix Aug 11 '16 at 04:38
  • @Felix: a class can have as many constructors as you want, as long as they take different inputs. A *default* constructor has special meaning, as it is called automatically when an instance of the class is created without any input values. When you declare an array like `Output output[2];`, its elements still have to be constructed like any other variable. Since the array is a member of `HighWaterDetector`, you have to provide the array element constructor inputs in the `HighWaterDetector` constructor's initialization list, otherwise they get default constructed instead. – Remy Lebeau Aug 11 '16 at 04:47
  • @Felix: the compiler automatically generates a default constructor for you - *unless* - you define a custom constructor instead, which you did. – Remy Lebeau Aug 11 '16 at 04:49