-1

I come from the python world where __str__ and __repr__ have been very useful to my development and execution workflow output. I would like to implement such things in C++. This post has been useful, however I would like that the string output includes the class name and that it be easily overloadable by subclasses. Here is a code example:

#include<iostream>
#include<string>

class Parent
{
    static constexpr const char* clsName = "Parent";
    std::string _label;
public:
    Parent(std::string& label) : _label(label) {}

    friend std::ostream &operator<<(std::ostream &os, 
                                    Parent const &ref)
    {   
        os << clsName << "(";
        ref.print(os); 
        os << ")";
        return os; 
    }
    void print(std::ostream &os) const
    { os << _label; }
};

class Child : public Parent
{
    static constexpr const char* clsName = "Child";
public:
    Child(std::string& label) : Parent(label) {}
};

My intent here is that the Child::operator<< use it own clsName static private data, without having to overload the whole operator for each subclass. Unfortunately this strategy does not work:

int main()
{
    std::string l("some label");
    Child x(l);
    std::cout << x << std::endl;
}

Will output

Parent(some label)

(I would like to see Child(some label)).

MartN
  • 1

2 Answers2

1

I would add a virtual member function to get the class name.

I would also make print a virtual member function to allow derived classes to enhance what's being done in the base class.

I suggest a couple more enhancements:

  1. Make the argument to the constructors a const&.
  2. The operator<< function does not need to be a friend.

Here's an updated version of your classes.

class Parent
{
   private:
      static constexpr const char* clsName = "Parent";
      std::string _label;

   public:
      Parent(std::string const& label) : _label(label) {}

      virtual std::string getClassName() const
      {
         return clsName;
      }

      virtual void print(std::ostream &os) const
      {
         os << _label;
      }
};

std::ostream &operator<<(std::ostream &os, Parent const &ref)
{   
   os << ref.getClassName() << "(";
   ref.print(os); 
   os << ")";
   return os; 
}

class Child : public Parent
{
   private:
      static constexpr const char* clsName = "Child";

   public:
      virtual std::string getClassName() const
      {
         return clsName;
      }

      virtual void print(std::ostream &os) const
      {
         // Nothing to do for this class in particual.
         // Just use the Parent implementation.
         Parent::print(os);
      }

      Child(std::string const& label) : Parent(label) {}
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

You need to make clsName a virtual function. Currently Child::clsName merely hides Parent::clsName, you can still access Parent::clsName from within Child.

You also should declare the destructor of Parent to be virtual, which means that the other special members also should be declared. All five can be implemented as = default.

#include<iostream>
#include<string>

class Parent
{
    virtual std::string clsName() const { return "Parent" };
    std::string _label;
public:
    Parent(const std::string& label) : _label(label) {}
    virtual ~Parent() = default;
    Parent(const Parent &) = default;
    Parent(Parent &&) = default;
    Parent& operator=(const Parent &) = default;
    Parent& operator=(Parent &&) = default;

    friend std::ostream &operator<<(std::ostream &os, Parent const &ref)
    {   
        return os << ref.clsName() << "(" << ref._label << ")";
    }
};

class Child : public Parent
{
    std::string clsName() const override { return "Child" };
public:
    using Parent::Parent;
};
Caleth
  • 52,200
  • 2
  • 44
  • 75