Please check the line(s) of code after the comments '//this is the line of code in question... '.

I want to know if this is the proper way to use the std::ostream operator<< in multi-inheritance? Basically I want the derived object(dog) to call the base object's std::ostream operators without hard-coding it....Please note the program works, I'm just looking for a verification. Is this the proper way to use the std::ostream operator in this program?

#include <iostream>

class animal
{
  public:
    animal(const char *name):itsname(name) {}
    virtual ~animal() {}
    
    friend std::ostream& operator<<(std::ostream & out, const animal & rhs);
  protected:
    const char *itsname;
};
class mammal:public animal
{
  public:
    mammal(const char *name, unsigned long val):animal(name), itsweight(val) {}
    virtual ~mammal() {}
    
    friend std::ostream& operator<<(std::ostream & out, const mammal & rhs);
  protected:
    unsigned long itsweight;
};
class dog:public mammal
{
  public:
    dog(const char *name, unsigned long weight, unsigned long age):mammal(name, weight), itsage(age) {}
    virtual ~dog() {}
    
    friend std::ostream& operator<<(std::ostream & out, const dog & rhs);
  protected:
    unsigned long itsage;
};

std::ostream& operator<<(std::ostream & out, const animal & rhs)
{
  //this is the line of code in question...  
  out<<"animal's name->"<<rhs.itsname;
  return out;
}
std::ostream& operator<<(std::ostream & out, const mammal & rhs)
{
  //this is the line of code in question...
  out<<*dynamic_cast<const animal*>(&rhs)<<" - "<<"mammal's weight->"<<rhs.itsweight;
  return out;
}
std::ostream& operator<<(std::ostream & out, const dog & rhs)
{
  //this is the line of code in question...
  out<<*dynamic_cast<const mammal*>(&rhs)<<" - "<<"dog's age->"<<rhs.itsage;
  return out;
}

int main(int argc, char**argv)
{
  dog mydog("vicious dog woof", 4321, 34);
  
  std::cout<<mydog<<"\n";
  return 0;
}

Program's output:

animal's name->vicious dog woof - mammal's weight->4321 - dog's age->34

What you are doing is hard coding each stream insertion operator. This is what you want :

struct Animal{
 int weight, age;
 string name;
 Animal(string name = "", int weight = 0, int age = 0)
 : name(name), weight(weight), age(age){}
 ~Animal()=0{}
};
struct Mammal : Animal{
};
struct Dog  : Mammal{
};
std::ostream& operator>>(std::ostream& outStream, const Animal& animal){
 outStream << "Name : " <<  animal.name << "\n";
 outStream << "Weight : " << animal.weight << "\n";
 return outStream << "Age : " << animal.age << "\n";
}
int main(){
 Dog dog("Rudolph",150,7);
 cout << dog << endl;
}

What you are doing is hard coding each stream insertion operator. This is what you want :

struct Animal{
 int weight, age;
 string name;
 Animal(string name = "", int weight = 0, int age = 0)
 : name(name), weight(weight), age(age){}
 ~Animal()=0{}
};
struct Mammal : Animal{
};
struct Dog  : Mammal{
};
std::ostream& operator>>(std::ostream& outStream, const Animal& animal){
 outStream << "Name : " <<  animal.name << "\n";
 outStream << "Weight : " << animal.weight << "\n";
 return outStream << "Age : " << animal.age << "\n";
}
int main(){
 Dog dog("Rudolph",150,7);
 cout << dog << endl;
}

Did you even read the posting? You hard coded the base objects std::osstream operator<< into the object dog...

Yes I did, and I showed you what you should have done. What you did made no sense.
Every animal has a name,weight, and age. Thus the animal class should contain all.
Instead of defining a operator<< for all three, you only need to define one operator<<.

So you see your structures does not really make sense. But to answer your question,
yes that is one way you can and did achieve what you wanted.

Yes I did, and I showed you what you should have done. What you did made no sense.
Every animal has a name,weight, and age. Thus the animal class should contain all.
Instead of defining a operator<< for all three, you only need to define one operator<<.

So you see your structures does not really make sense. But to answer your question,
yes that is one way you can and did achieve what you wanted.

I want each object/class to define its own std::ostream operator<< and the derived objects to call/incorporate them into their own std::ostream operator<<. This way you can change the base classes std::ostream operator<< without changing the derived objects...Do you get what I'm driving at?

@firstPerson: the point is not if the members make sense or not for the classes, it's about how to best implement the I/O for base and derived classes.

@gerard: Here is what you should be doing, in my opinion (and this is what I actually do in my own code):

//IO interface for all the classes:
class printable {
  public:
    //this pure virtual method will be called to print the derived class' object.
    virtual std::ostream& print(std::ostream& output) const = 0;
};
//with overloaded operators:
std::ostream& operator<<(std::ostream & out, const printable & rhs) {
  return rhs.print(out); //simply call the print method.
};


//then your own class hierarchy:
class animal : public streamable
{
  public:
    animal(const char *name):itsname(name) {}
    virtual ~animal() {}
    
    //override the print function in each class that has any new data members.
    std::ostream& print(std::ostream& output) const {
      return output << "animal's name->" << itsname;
    };
  protected:
    const char *itsname;
};
class mammal:public animal
{
  public:
    mammal(const char *name, unsigned long val):animal(name), itsweight(val) {}
    virtual ~mammal() {}
    
    //override the print function in each class that has any new data members (and call the base class method too).
    std::ostream& print(std::ostream & output) const {
      return animal::print(output) << " - " << "mammal's weight->" << itsweight;
    };
  protected:
    unsigned long itsweight;
};
class dog:public mammal
{
  public:
    dog(const char *name, unsigned long weight, unsigned long age):mammal(name, weight), itsage(age) {}
    virtual ~dog() {}
    
    //override the print function in each class that has any new data members (and call the base class method too).
    std::ostream& print(std::ostream & output) const {
      return mammal::print(output) << " - " << "dog's age->" << itsage;
    };
  protected:
    unsigned long itsage;
};

int main(){
 Dog dog("Rudolph",150,7);
 cout << dog << endl;
};
commented: Yes. +22

Thanks for the reply Mike..I'm looking at your solution right now.

Note: I found a few typos but the code works..Thanks...You might want to verify the corrections.

#include <iostream>

//IO interface for all the classes:
class printable 
{
	public:
		//this pure virtual method will be called to print the derived class' object.
		virtual std::ostream& print(std::ostream& output) const = 0;
};

//with overloaded operators:
std::ostream& operator<<(std::ostream & out, const printable & rhs) 
{
	return rhs.print(out); //simply call the print method.
}


//then your own class hierarchy:
class animal : public printable
{
	public:
		animal(const char *name):itsname(name) {}
		virtual ~animal() {}

	//override the print function in each class that has any new data members.
		std::ostream& print(std::ostream& output) const 
		{
			return output << "animal's name->" << itsname;
		};
	protected:
		const char *itsname;
};

class mammal:public animal
{
	public:
		mammal(const char *name, unsigned long val):animal(name), itsweight(val) {}
		virtual ~mammal() {}

		//override the print function in each class that has any new data members (and call the base class method too).
		std::ostream& print(std::ostream & output) const 
		{
			return animal::print(output) << " - " << "mammal's weight->" << itsweight;
		};
	protected:
		unsigned long itsweight;
};
class Dog:public mammal
{
	public:
		Dog(const char *name, unsigned long weight, unsigned long age):mammal(name, weight), itsage(age) {}
		virtual ~Dog() {}

		//override the print function in each class that has any new data members (and call the base class method too).
		std::ostream& print(std::ostream & output) const 
		{
			return mammal::print(output) << " - " << "dog's age->" << itsage;
		};
	protected:
		unsigned long itsage;
};

int main()
{
	 Dog dog("Rudolph",150,7);
	 std::cout << dog << std::endl;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.