I'm implementing a simple hierarchy: System and Device would be two base classes. A System has Devices. Then I'd have a Network derived from System, which will have Computers. Computer is derived from both System and Device. And finally a Computer has various Devices like CPU, Memory etc. The reason Computer is a Device is because Network is a System and Systems store Devices. The reason Computer is a System is because it stores Devices (CPU, Memory etc.)
Anyway, my implementation so far:
class Device {
public:
explicit Device(unsigned property): property(property) {}
friend void swap(Device &first, Device &second) {
using std::swap;
swap(first.property, second.property);
}
Device& operator=(Device other) {
swap(*this, other);
return *this;
}
private:
Device() = default;
unsigned property;
};
class System {
public:
explicit System(const string &name): name(name) {}
friend void swap(System &first, System &second) {
using std::swap;
swap(first.name, second.name);
swap(first.devices, second.devices);
}
System& operator=(System other) {
swap(*this, other);
return *this;
}
protected:
void addDevice(Device b1) {
devices.push_back(b1);
}
private:
vector<Device> devices;
string name;
};
class Computer: public System, public Device {
public:
Computer(unsigned property, const string& name): System(name), Device(property) {}
Computer& addAddress(const string &address) {
addresses.emplace_back(address);
return *this;
}
Computer& addComponent(Device newDevice) {
System::addDevice(newDevice); //error
return *this;
}
private:
vector<const string> addresses;
};
class Network: public System {
public:
Network(const string& name): System(name) {}
Network& addComputer(Device newComputer) {
System::addDevice(newComputer); //no error
return *this;
}
};
class CPU: public Device {
public:
CPU(unsigned coreCount, unsigned frequency): Device(coreCount), frequency(frequency) {}
private:
unsigned frequency;
};
Computer's addComponent() has an error because cannot initialize object parameter of type System with an expression of type Computer
. I'm not entirely sure what that means since that function is called from a Computer class, which is a Device hence it should have access to the parent's addDevice(). The parameter the function has is a Device.
It's also confusing that my Network class, where the function addComputer() uses the same set of instructions has no error.
Could someone explain how my design is flawed?
This error message popped up for me today, while I was in the processes of coding the derived class, and in my case the key was that the derived class was--just at that moment--un-instantialble.1
As soon as I made enough progress on the derived class for it to be instantiated the errors evaporated.
The derived call from which you are typing to invoke the base class is currently un-instantiable.
Consider the following code which has two inheritance relationships.
class Type {
public:
Type(){}
virtual ~Type() {}
virtual void m() = 0;
};
class SubType: public Type {
public:
SubType(): Type() {}
virtual ~SubType() {}
// virtual void m() override; // <== Important code commented out here
};
// void SubType::m() {} // <== and here
class Base {
public:
Base() {};
virtual ~Base() {}
virtual void m();
};
class Derived: public Base, public Type {
public:
Derived(): Base() {}
virtual ~Derived() override {}
virtual void m() override;
protected:
SubType m_troublesome; // <== Note member here has incomplete type when
// comments are in place
};
void Base::m() {}
void Derived::m() {
Base::m(); // <== Tricky error message on this line
}
Attempt to compile this with
$ clang --std=c++11 -Wall -pedantic -c test.cc
and you receive the feedback
test.cc:34:13: error: field type 'SubType' is an abstract class
SubType m_troublesome;
^
test.cc:8:18: note: unimplemented pure virtual method 'm' in 'SubType'
virtual void m() = 0;
^
test.cc:42:11: error: cannot initialize object parameter of type 'Base' with an
expression of type 'Derived'
Base::m();
If you see all three together you know where to start, but if you are using clang as a linter (as many IDEs do), then you may see the third one (which occurs on the line Base::m();
, a perfectly valid piece of code in isolation) in a context divorced from the first two.
Instant confusion.
If you remove the comments from the two marked sections of code (making SubType
complete and thus making it possible to instantiate Derived
objects), the code compiles cleanly.
SubType
half done the situation would never have arisen.1 In particular I had a member declared with a type in which I hadn't yet overridden an inherited abstract virtual method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With