Default value for abstract class pointer parameter

Viewed 245

I am trying to do something like this:

class Movement {
public:
    virtual void move() = 0;
};

class Walk : public Movement {
public:
    void move() { cout << "walking"; }
};

class Run : public Movement {
public:
    void move() { cout << "run"; }
};
class Animal {
public:
    virtual void print();
};

class Human : public Animal {
public:
    void print() { cout << "Human"; }
};

class Lion : public Animal {
public:
    void print() { cout << "Lion"; }
};
class Model {
    Animal* animal;
    Movement* movement;

public:
    Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
        this->animal = animal;
        this->movement = movement;
    }
    void print() {
        cout << "This Model consist of one: ";
        animal->print();
        cout << ", which is: ";
        movement->move();
    }
};
int main() {
    Model first = Model(), second = Model(new Lion(), new Run());
    first.print();
    cout << endl;
    second.print();
    return 0;
}

How do we set the default value for abstract class pointers & how to pass them as a parameter like that from main?

I would also prefer to be able to pass arguments from main like this only in a single line without needing to initialize before.

can anyone please help me with how do we such things in C++?

I have tried and searched a lot but no luck.

I am looking for a workaround to do something like this, in which we use an abstract classes as a parameter of other classes.

I know objects cannot be assigned to a pointer, I just don't know what to do there to fulfill my requirement, an abstract class as a parameter with a default value.

This is my latest attempt with exact code, but unfortunately with new, does anyone know how to get rid of new and achieve the desired outcome?

Note:
My actual code is quite complex, basically using an abstract class for polymorphism and pass those abstract classes as parameters to another class with default parameters, if there is ANY other way to do something similar I would really appreciate the help.

3 Answers

This is really a design question. In Modelclass design, you either need to decide about the object ownership, or defer the decision to the calling code. In the latter case, you cannot have default arguments (unless you want to have global constants Human and Walk, but I would not recommend it).

One way to have the default arguments is to decide that Model has exclusive ownership of Animal and Movement, and store unique_ptrs to them. Something like this:

class Model {
    unique_ptr<Animal> animal;
    unique_ptr<Movement> movement;

public:
  Model(unique_ptr<Animal> animal = make_unique<Human>(), unique_ptr<Movement> movement = make_unique<Walk>()){ 
    this->animal = std::move(animal);
    this->movement = std::move(movement);
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first/*no () here!*/, second(make_unique<Lion>(), make_unique<Run>()); 
  first.print();
  cout << endl;
  second.print();
  return 0;
}

I think I came up with the best solution for my situation.

#include <iostream>
#include <memory>
using namespace std;
class Movement {
 public:
  virtual void move() = 0;
  virtual unique_ptr<Movement> movement() const = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
  unique_ptr<Movement> movement() const { return make_unique<Walk>(); }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
  unique_ptr<Movement> movement() const { return make_unique<Run>(); }
};
class Animal {
 public:
  virtual void print() = 0;
  virtual unique_ptr<Animal> animal() const = 0;
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
  unique_ptr<Animal> animal() const { return make_unique<Human>(); }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
  unique_ptr<Animal> animal() const { return make_unique<Lion>(); }
};
class Model {
  unique_ptr<Animal> animal;
  unique_ptr<Movement> movement;

 public:
  Model(const Animal& animal = Human(), const Movement& movement = Walk()) {
    this->animal = animal.animal();
    this->movement = movement.movement();
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};
int main() {
  Model first = Model(), second = Model(Lion(), Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

Is your problem the compile error? There are multiple ways to address the compile error, but given that your question is about inheriting from abstract classes, I will focus on that.

First, as provided, your Animal class is not an abstract class. An abstract class cannot be instantiated because all its methods are pure virtual. In C++, pure virtual functions are designated by the virtual keyword prefix, and suffixed by = 0 in their definition. E.g.

...
virtual void print() = 0;
...

The following code is compilable by making your Animal class an abstract class:

#include <iostream>
using namespace std;

class Movement {
 public:
  virtual void move() = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
};

class Animal {
 public:
  virtual void print() = 0;
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
};

class Model {
  Animal* animal;
  Movement* movement;

 public:
  Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
    this->animal = animal;
    this->movement = movement;
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first = Model(),
        second = Model(new Lion(), new Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

Incidentally, your code can also be made compilable by providing an implementation for Animal::print(). The following code is also compilable, but Animal is not an abstract class because it provides an implementation for Animal::print() rather than suffixing it with = 0:

#include <iostream>
using namespace std;

class Movement {
 public:
  virtual void move() = 0;
};

class Walk : public Movement {
 public:
  void move() { cout << "walking"; }
};

class Run : public Movement {
 public:
  void move() { cout << "run"; }
};

class Animal {
 public:
  virtual void print() {};
};

class Human : public Animal {
 public:
  void print() { cout << "Human"; }
};

class Lion : public Animal {
 public:
  void print() { cout << "Lion"; }
};

class Model {
  Animal* animal;
  Movement* movement;

 public:
  Model(Animal* animal = new Human(), Movement* movement = new Walk()) {
    this->animal = animal;
    this->movement = movement;
  }
  void print() {
    cout << "This Model consist of one: ";
    animal->print();
    cout << ", which is: ";
    movement->move();
  }
};

int main() {
  Model first = Model(),
        second = Model(new Lion(), new Run());
  first.print();
  cout << endl;
  second.print();
  return 0;
}

Otherwise, conceptually, what you're doing is fine and totally possible in C++: assigning a default value to a base class pointer that's in some function's argument list.


Important: As commenters have correctly pointed out, the pattern you have coded is dangerous: your interface is such that a user can optionally provide an Animal instance. The problem is: if the Model creator does, then it can be reasonably argued that he rightly owns the object. If he does not, then your constructor will create a new Animal instance, but neither does Model take ownership of the object, nor does it provide an interface by which the user can take ownership of the new Animal instance. This therefore creates a memory leak. Equally, the code hazard, is ambiguous ownership of the Animal instance used in the Model constructor.

Related