Search code examples
c++derived-classinitializer-list

mixing c++ class inheritance and initializer lists. An unsolved puzzle in my mind


I have 4 classes

class A { //a base class
    public:
    A(ostringstream* os){ oss2=os; }
    ostringstream* oss2;
};

class B : public A{ // a derived class 
    public:
    B(ostringstream* os):A(os){};
};

class C { //class where oss is actually declared
    public:
    C(){};
    ostringstream oss;
};

and finally (the kicker): a derived class from "C" , that needs to pass a reference to oss around.

class D : public C{
    public:
    B b(&oss);
};

in line B b(&oss); I get the error

error: expected identifier before ‘&’ token

I honestly don't know if I am playing with fire here. The basic idea is that I want some functions of A to write to a common output stream.

This is not a logging class but I see that some loggers declare a static oss variable and allow everyone to write there. I would prefer to avoid that and instantiate my oss in the (logically) correct location. In my case, that is in class C.

I think (but I could be wrong) that the error here is that I am trying to provide a reference to a variable (oss) through the declaration of B b. Is that right? Instead an initializer list should be used to provide the value to the constructor. But I am not sure how to implement this (and test if it compiles).


Solution

  • If you want to have a field of type B in D, you should just declare it there and initialize it in the constructor, as you already hinted at:

    class D : public C{
      public:
        D() : 
          C(),    // You can leave this out, the compiler will do it for you
          b(&oss) // Once the C part is constructed you can pass in its field.
        {}
    
      private: // ?
        B b;
    };
    

    But depending on your situation, there may be a better solution in terms of design. For example, if C is a base class for output, and A is a class that needs to do some logging, you could go with a solution like this:

    class A { // worker base class
        public:
            A(C* logger) : logger(logger) {}
        private:
            C* logger;
    };
    
    class B : public A{ // a derived class 
        public:
            B(C* logger) : A(logger) {};
    };
    
    class C { // logging class
        public:
            C(){};  
            virtual ~C() {};
            virtual void Log(const std::string& message) = 0;
    };
    
    class D : public C { // a derived class
      public:
          D() {} 
          virtual void Log(const std::string& message) 
          {
            this->oss << message;
          }
      private:
          ostringstream oss;
    }
    
    // In main:
    D streamLogger;
    A worker(&streamLogger);
    

    Now A doesn't care what it outputs to, just pass it a class which supports Logging a message and let polymorphism handle the rest. In fact, if you want to have a logging method that sends emails, just create a new derivative of C and pass it into the same A instance.