Reputation: 195
#include <iostream>
#include <string>
using namespace std;
class Part{
public:
std::string spec;
Part(std::string str){
this->spec = str;
}
std::string getSpec(){
return spec;
}
};
class Car{
public:
Part getEngine();
Part getWheels();
Part getBody();
};
class Benz:public Car{
public:
Part getEngine(){
return Part("Benz Engine");
}
Part getWheels(){
return Part("Benz Wheels");
}
Part getBody(){
return Part("Benz Body");
}
};
class Audi:public Car{
public:
Part getEngine(){
return Part("Audi Engine");
}
Part getWheels(){
return Part("Audi Wheels");
}
Part getBody(){
return Part("Audi Body");
}
};
class CarFactory{
public:
Car *pcar;
Car *getCar(std::string carType){
if (carType.compare("Benz") == 0){
pcar = new Benz();
}else if (carType.compare("Audi") == 0){
pcar = new Audi();
}
return pcar;
}
};
int main(){
CarFactory *cf = new CarFactory();
Car *mycar = cf->getCar("Benz");
Part myCarBody = mycar->getBody();
cout <<myCarBody.getSpec() <<endl;
//cout << mycar->getBody().getSpec() <<endl;
}
In the above code getting, undefined reference to object error at line Part myCarBody = mycar->getBody(); line of main function. Can you please help me for fixing this and also please explain the difference between Car *mycar and Car mycar. Which one to choose when?
Thanks for the help.
Upvotes: 0
Views: 1382
Reputation: 19607
The member functions of Car
should be pure virtual as opposed to your non-virtual declarations:
class Car
{
public:
virtual ~Car() = default; // Virtual destructor
virtual Part getEngine() = 0;
virtual Part getWheels() = 0;
virtual Part getBody() = 0;
};
That's why the linker error. Compiler expects Car
's function definitions because the functions aren't pure virtual, but it couldn't find it.
With pure virtual member functions, Car
becomes an abstract class and provides an interface (pure virtual), that must be implemented by classes derived from it. Otherwise, the derived class will be abstract too and not instantiable.
By calling a virtual function of a base class, you end up calling a function of a derived class that overrides it. Destructor is also the case and needs to be made virtual, in order to call the destructor of the derived class and avoid undefined behavior. If you had a dynamically allocated data in one of the derived classes, there would be a memory leakage.
Also, you have to use a pointer or a reference when dealing with polymorphism. The logic behind it is that pointers have the same size, but a derived class like Benz
could have additional data and not fit in the space occupied by Car
and would become sliced. Thus, assignment/copy construction do not work properly without a pointer or reference.
Suggested in comments:
Accessor functions ("getters" and "setters") come usually with
private or protected members. Yours are public, the accessor is
useless and can be circumvented. Keep the accessor, make the data
private. When you're returning a member, you can do it by
reference-to-const. But don't do it for getEngine
, getWheels
and
getBody
, because they're returning a local object, not a member.
std::string const& getSpec() { return spec; }
While we're at it, you can pass std::string
by reference-to-const too.
Part(std::string const& str) : spec(str) {}
Factory class
class CarFactory
{
public:
Car *getCar(std::string const& carType)
{
if(carType == "Benz") // std::string::compare is for comparing by alphabetical order
return new Benz();
if(carType == "Audi")
return new Audi();
return nullptr; // invalid carType, return nullptr and check for that in main()
// You were returning previously constructed Car, was that desired?
}
};
You're also forgetting to delete myCar
, see more good info in the other answer.
Upvotes: 6
Reputation: 3825
First if you want to use polymorphism you need to declare functions as virtual.
If you don't plan to implement methods in base class you may declare them as pure virtual. This make your base class so called "abstract" and you will can't create objects of that class (in your example best candidate is Car object).
Also beware. If you want to destroy objects via pointer to base class you also need virtual destructor.
So to put all together you need:
class Car {
public:
virtual ~Car() {}
virtual Part getEngine() = 0;
virtual Part getWheels() = 0;
virtual Part getBody() = 0;
};
So the root cause of your problem that you said to compiler "Hey, I'm going to implement methods of Car" but hadn't done it. So compiler expected to call that methods from your main but on linking stage linker can't find them.
Another problems you need to think about:
Upvotes: 5