0

I am interested to write a printer to output two different formats. Let's call them txt and csv.

Can something like below be implemented in C++?

class Printer {
public:
  virtual void header() = 0;
};

class CSV : Printer {
public:
 void header() { printf("csv\n"); }
};

class TXT : Printer {
public:
 void header() { printf("txt\n"); }
};

int decider(int type) {
  auto prt;
  if (type == 1) {
     prt = new CSV;
  } else {
     prt = new TXT;
  }
  prt.header();
}

If not, what's an alternative to doing something similar?

4 Answers4

5

Instead of auto you'd want a pointer to the base class

int decider(int type) {
  Printer* prt;
  if (type == 1) {
     prt = new CSV;
  } else {
     prt = new TXT;
  }
  prt->header();
}

but instead of raw pointers and new (since you are leaking memory from a missing delete) I'd instead use smart pointers here

#include <memory>

int decider(int type) {
  std::unique_ptr<Printer> prt;
  if (type == 1) {
     prt = std::make_unique<CSV>();
  } else {
     prt = std::make_unique<TXT>();
  }
  prt->header();
}

As noted by @FredLarson you also need a virtual destructor in your base class

class Printer {
public:
  virtual ~Printer() = default;
  virtual void header() = 0;
};

you should also inherit publicly from the base class

class CSV : public Printer { 
  ...
};

class TXT : public Printer {
  ...
};
Cory Kramer
  • 98,167
  • 13
  • 130
  • 181
2

This is the right way:

void decider(int type) {
  std::unique_ptr<Printer> prt{};
  if (type == 1) {
     prt = std::make_unique<CSV>();
  } else {
     prt = std::make_unique<TXT>();
  }
  prt->header();
}
Nathan Xabedi
  • 1,060
  • 1
  • 7
  • 14
  • 2
    I believe `Printer` will need a virtual destructor to avoid UB here. – Fred Larson Apr 08 '21 at 18:58
  • 1
    Extensions: Consider returning the `unique_ptr` rather than calling the `header` function. This will allow you to reuse `decider` for similar tasks in the future. Also consider using an `enum` for the type rather than an `int`. – user4581301 Apr 08 '21 at 19:12
2

Yes, this kind of polymorphism is possible. Here is a runnable example based on yours:

#include <stdio.h>

class Printer {
public:
  virtual void header() {};
};

class CSV : public Printer {
public:
 void header() { printf("csv\n"); }
};

class TXT : public Printer {
public:
 void header() { printf("txt\n"); }
};

int decider(int type) {
  Printer* prt;
  if (type == 1) {
     prt = new CSV();
  } else {
     prt = new TXT();
  }
  prt->header();
}

int main() {

    int type = 1;

    decider(type);

    type = 0;

    decider(type);
}
Leo Lamas
  • 129
  • 8
0

What about passing the required type as template parameter?

I mean something as

template <typename T>
void decider ()
 {
   auto prt = new T;
   prt.header();

   delete prt;
 }

that you can call as follows

decider<CSV>();
decider<TXT>();

As pointed by Daniel H (thanks!) this only works if you know the type compile-time; if the type (CSV or TXT) is decided run-time, this solution is impracticable.

max66
  • 60,491
  • 9
  • 65
  • 95