Why can I not print the derived object using this approach? How can I fix this so that the derived printout prints "derived size=8". I ideally want to keep the printing code out of the class code.
#include <iostream>
class base
{
public:
base(int size) : size_(size) {}
virtual int get_size() const { return size_; }
private:
int size_;
};
std::ostream& operator<<(std::ostream& os, const base& obj) {
os << "base size=" << obj.get_size() << std::endl;
return os;
}
class derived1 : public base
{
public:
derived1(int size) : base(size) {}
virtual int get_size() const {
return 2 * base::get_size();
}
};
std::ostream& operator<<(std::ostream& os, const derived1& obj) {
os << "derived1 size=" << obj.get_size() << std::endl;
return os;
}
int main(int argc, char* argv[]) {
base* b1 = new base(3);
std::cout << "b1 size is: " << b1->get_size() << std::endl;
std::cout << *b1 << std::endl;
base* b2 = new derived1(4);
std::cout << "b2 size is: " << b2->get_size() << std::endl;
std::cout << *b2 << std::endl;
delete b1;
delete b2;
return 0;
}
Output:
b1 size is: 3
base size=3
b2 size is: 8
base size=8
UPDATE:
I changed as follows as per ghostsofstandardspast which works:
#include <iostream>
class base
{
public:
base(int size) : size_(size) {}
virtual int get_size() const { return size_; }
friend std::ostream& operator<<(std::ostream &os, const base& obj);
private:
virtual std::ostream &print(std::ostream &os) const {
return os << "base size=" << get_size() << std::endl;
}
int size_;
};
std::ostream& operator<<(std::ostream& os, const base& obj) {
obj.print(os);
return os;
}
class derived1 : public base
{
public:
derived1(int size) : base(size) {}
virtual int get_size() const {
return 2 * base::get_size();
}
friend std::ostream& operator<<(std::ostream &os, const derived1& obj);
private:
virtual std::ostream &print(std::ostream &os) const {
return os << "derived1 size=" << get_size() << std::endl;
}
};
int main(int argc, char* argv[]) {
base* b1 = new base(3);
std::cout << "b1 size is: " << b1->get_size() << std::endl;
std::cout << *b1 << std::endl;
base* b2 = new derived1(4);
std::cout << "b2 size is: " << b2->get_size() << std::endl;
std::cout << *b2 << std::endl;
return 0;
}
operator<< is not a virtual member function, so it can't be used polymorphically like other member functions can. Instead, you should delegate to a virtual member.
Change both of your operator<< overloads to a virtual member function called print or something similar. Then, overload operator<< to delegate to that:
std::ostream &operator<<(std::ostream &os, const Base &obj) {
obj.print(os); //or whatever name you choose
return os;
}
If you want to make the member function private, make operator<< a friend.
Finally, note that printing a pointer to an object of your class will only print the address. To invoke operator<<, you need to print the object itself:
Base *b = /*whatever*/;
std::cout << *b;
This technique for a polymorphic operator<< is demonstrated in this cppquiz question. I highly recommend going through those questions.
cout << b2 doesn't invoke your operator <<, because b2 is a pointer not a reference. Try cout << *b2 instead.
It doesn't work because in a call
std::cout << b1 << std::endl;
you are printing an address, since b1 is a pointer ( i.e. 0x8e02008).
You should dereference it instead
std::cout << *b1 << std::endl;
In this case both for *b1 and *b2 objects the std::ostream& operator<<(std::ostream& os, const base& obj); will be calles and virtual call to get_size() will be executed.
ideone
Related
I have a parent DataType class from which I inherit Data Type Int, DataType Double, DataTypeEnum
and CDataTypeStruct. Somewhere I use the print () method defined by the parent and somewhere I rewrite it. I call the print method using the << operator.
Why, when I call a title for the CDataTypeEnum type, everything is displayed correctly, as I have the print defined in the CDaraTypeEnum.
I get this
struct {
int int enum}
but if I want to list cout << structure << endl; so for the CDataTypeStruct type, I don't get an overloaded print method for each object?
Just to make the statement look like this
struct {
int int
enum {
NEW,
FIXED,
BROKEN,
DEAD
}
}
--
All program https://onecompiler.com/cpp/3y2rhbm7a and here's a minimal reproducible example:
#include <iostream>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>
using namespace std;
class CDataType
{
public:
CDataType(string type, size_t size);
friend ostream& operator << (ostream &os, CDataType &x);
virtual ostream& print (ostream &os) const;
protected:
string m_Type;
size_t m_Size;
};
CDataType::CDataType(string type, size_t size)
: m_Type(type),
m_Size(size)
{
}
ostream& operator << (ostream &os, CDataType &x)
{
x.print(os);
return os;
}
ostream& CDataType::print (ostream &os) const
{
os << m_Type;
return os;
}
class CDataTypeInt : public CDataType
{
public:
CDataTypeInt();
};
CDataTypeInt::CDataTypeInt()
: CDataType("int", 4)
{
}
class CDataTypeEnum : public CDataType
{
public:
CDataTypeEnum();
CDataTypeEnum& add(string x);
virtual ostream& print (ostream &os) const;
protected:
vector<string> listEnums;
set<string> listEnumsNames;
};
CDataTypeEnum::CDataTypeEnum()
: CDataType("enum", 4)
{
}
ostream& CDataTypeEnum::print(ostream &os) const
{
os << m_Type << "{\n";
for (auto i=listEnums.begin(); i != listEnums.end(); ++i )
{
os << *i;
if(i != listEnums.end()-1)
{
os << ",";
}
os << "\n";
}
os << "}";
return os;
}
CDataTypeEnum& CDataTypeEnum::add(string x)
{
if(listEnumsNames.find(x) == listEnumsNames.end())
{
listEnums.push_back(x);
listEnumsNames.emplace(x);
}
else
cout << "vyjimkaa" << endl;
// CSyntaxException e("Duplicate enum value: " + x);
return *this;
}
class CDataTypeStruct : public CDataType
{
public:
virtual ostream& print (ostream &os) const;
CDataTypeStruct();
CDataTypeStruct& addField(const string &name, const CDataType &type);
protected:
list<unique_ptr<CDataType>> m_Field;
unordered_set<string> m_Field_names;
};
CDataTypeStruct::CDataTypeStruct()
:CDataType("struct", 0)
{
}
CDataTypeStruct& CDataTypeStruct::addField(const string &name, const CDataType &type)
{
if( m_Field_names.find(name) == m_Field_names.end() )
{
m_Field.push_back(make_unique<CDataType>(type));
m_Field_names.emplace(name);
}
// else
//throw CSyntaxException("Duplicate field: " + name);
return *this;
}
ostream& CDataTypeStruct::print (ostream &os) const
{
os << m_Type << "{\n";
for(const auto &uptr : m_Field)
{
uptr->print(os) << " " /*<< "{\n"*/;
}
os << "}";
return os;
}
int main()
{
CDataTypeInt inta;
CDataTypeInt intb;
CDataTypeStruct struktura;
CDataTypeEnum enumos;
enumos.add( "NEW" ).add ( "FIXED" ) .add ( "BROKEN" ) .add ( "DEAD" );
struktura.addField("integera", inta);
struktura.addField("integerb", intb);
struktura.addField("bbb", enumos);
cout << enumos << endl;
cout << struktura << endl;
}```
As I already suggested in a similar question you posted already (and deleted), you're again slicing here:
struktura.addField("integera", inta); // Slicing
struktura.addField("integerb", intb); // Slicing
struktura.addField("bbb", enumos); // Slicing
Again, consider following guideline "A polymorphic class should suppress public copy/move" from the C++ Core Guidelines. To do this:
class CDataType {
public:
// ...
// Disable move and copy
CDataType(CDataType const &) = delete;
CDataType(CDataType &&) = delete;
CDataType& operaror=(CDataType const &) = delete;
CDataType& operaror=(CDataType &&) = delete;
// Dtor should be virtual.
virtual ~CDataType() = default;
// ...
};
Then, adapt your code accordingly (as it won't compile anymore).
Also, The destructor of CDataType should be virtual.
Edit: Please consider the following example, which hopeful makes the slicing issue clearer:
#include <cstdio>
#include <list>
#include <memory>
struct A {
A() = default;
A(A const&) { std::puts("A(A const&)"); }
virtual ~A() { std::puts("~A()"); }
};
struct B : public A {
B() = default;
B(B const&) { std::puts("B(B const&)"); }
~B() override { std::puts("~B()"); }
};
void slicing_addField(A const& a) {
std::puts("slicing_f");
std::list<std::unique_ptr<A>> l;
l.push_back(std::make_unique<A>(a));
}
void non_slicing_addField(std::unique_ptr<A> a) {
std::puts("non_slicing_f");
std::list<std::unique_ptr<A>> l;
l.push_back(std::move(a));
}
int main() {
// This is what you do
slicing_addField(B{});
// This is how you may solve
non_slicing_addField(std::make_unique<B>());
}
Output:
slicing_f
A(A const&)
~A()
~B()
~A()
non_slicing_f
~B()
~A()
As you can see from the output, you're only calling A(A const&) in slicing_addField.
This means the call to make_unique<A>(a) is allocating an object of run-time type A (while you want it of run-time type B).
The main problem is that you try to copy the CDataType decendant in addField by using make_unique<CDataType>, but that actually creates a CDataType, not a CDataTypeInt, CDataTypeEnum or CDataTypeStruct.
Since constructors can't be virtual (in standard C++) you need to create a separate virtual function to do the copying. A common name for such a function is clone. Example:
class CDataType {
public:
CDataType(string type, size_t size);
CDataType(const CDataType&) = delete;
CDataType& operator=(const CDataType&) = delete;
virtual ~CDataType() = default; // add virtual dtor
virtual std::unique_ptr<CDataType> clone() const {
return std::make_unique<CDataType>(m_Type, m_Size);
}
// ...
};
class CDataTypeEnum : public CDataType {
public:
using CDataType::CDataType;
std::unique_ptr<CDataType> clone() const override {
// use make_unique to create the correct type:
auto np = std::make_unique<CDataTypeEnum>(m_Type, m_Size);
np->listEnums = listEnums;
np->listEnumsNames = listEnumsNames;
return np;
}
// ...
};
class CDataTypeStruct : public CDataType {
public:
using CDataType::CDataType;
std::unique_ptr<CDataType> clone() const override {
// use make_unique to create the correct type:
auto np = std::make_unique<CDataTypeStruct>(m_Type, m_Size);
np->m_Field_names = m_Field_names;
for(auto& cdtp : m_Field)
np->m_Field.emplace_back(cdtp->clone()); // use clone here
return np;
}
// ...
};
Then addField would look like this:
CDataTypeStruct& CDataTypeStruct::addField(const string& name,
const CDataType& type) {
if (m_Field_names.find(name) == m_Field_names.end()) {
m_Field.emplace_back(type.clone()); // and use clone here
m_Field_names.emplace(name);
}
return *this;
}
After reading C++ auto deduction of return type and C++ : Vector of template class, I'm still wondering how to do generic operations (e.g. operator << overload) on object. My code looks like
#include <map>
#include <memory>
#include <string>
#include <iostream>
/**
* Abstract placeholder for Cache polymorphism
*/
class ICache
{
public:
virtual void update() {};
friend std::ostream & operator << (std::ostream & out, const ICache & IC)
{
out << "you should never print this";
}
};
/**
* Concrete. Coupling a name with some cached value
*/
template<typename T>
class Cache :
public ICache
{
const std::string m_name;
T m_cached;
public:
Cache(const std::string & name) :
m_name(name),
m_cached(0)
{}
void update() override
{
// m_cached is synced with remote resource; action depends both from T and m_name
}
void set(const T t)
{
std::cout << m_name << " setting " << +m_cached << " -> " << +t << std::endl;
m_cached = t;
}
inline T get() const noexcept { return m_cached; }
friend std::ostream & operator << (std::ostream & out, const Cache & O)
{
out << "Cache<" << O.m_name << ", " << O.m_cached << ">";
}
};
class CacheMap
{
std::map<std::string, std::unique_ptr<ICache>> m_map;
template<typename T>
Cache<T>* _get_ptr(const std::string & name) const
{
return reinterpret_cast<Cache<T>*>(m_map.at(name).get());
}
public:
template<typename T>
T get(const std::string & name) const
{
return _get_ptr<T>(name)->get();
}
template <typename T>
void set(const std::string & name, T t)
{
_get_ptr<T>(name)->set(t);
}
template <typename T>
void insert(const std::string & name, T def = 0)
{
std::unique_ptr<ICache> up = std::make_unique<Cache<T>>(name);
m_map.insert({name, std::move(up)});
set<T>(name, def);
}
friend std::ostream & operator << (std::ostream & out, const CacheMap & OM)
{
out << "OM{";
for (const auto & IO : OM.m_map)
out << IO.first << ": " << *(IO.second.get()) << ", "; // ver1
// out << IO.first << ": " << (IO.second->get()) << ", "; // ver2
out << "}";
return out;
}
};
int main()
{
CacheMap m;
int i= 70000;
m.insert<int>("i", 69999);
m.insert<short>("s", 699);
m.insert<char>("c", 69);
m.set("i", i);
std::cout << m << std::endl;
}
Line marked with trailing //ver1 shows you should never print this which makes sense; I'm dealing with std::unique_ptr<ICache> objects.
Line marked with trailing //ver2 won't compile at all, and this makes sense too.
What I'd like to do inside CacheMap is to auto-detect, at runtime (hmm sounds bad) the correct T, given a map key, in order to fire the right reinterpret_cast<> and to retrieve m_cached value.
EDIT 1
If compiling with g++ -O3, ver1 line causes a segmentation violation.
Just use a virtual function. The time you cast your variable from Cache<int> pointer type to ICache pointer you lose compile time information about it. That information is lost. You can use dynamic_cast in your friend ICache::operator<< to handle all the different types... To properly resolve type information use virtual functions - ie. unique data tied to each of the class.
#include <map>
#include <memory>
#include <string>
#include <iostream>
class ICache
{
public:
virtual ~ICache() {};
virtual void update() {};
// -------- HERE --------------
virtual std::ostream& printme(std::ostream & out) const = 0;
friend std::ostream& operator << (std::ostream & out, const ICache & IC) {
return IC.printme(out);
}
};
template<typename T>
class Cache : public ICache {
const std::string m_name;
T m_cached;
public:
Cache(const std::string & name): m_name(name), m_cached(0) {}
void update() override {}
void set(const T t) {
std::cout << m_name << " setting " << +m_cached << " -> " << +t << std::endl;
m_cached = t;
}
inline T get() const noexcept { return m_cached; }
std::ostream& printme(std::ostream & out) const override {
out << "Cache<" << m_name << ", " << m_cached << ">";
return out;
}
};
class CacheMap {
std::map<std::string, std::unique_ptr<ICache>> m_map;
template<typename T>
Cache<T>* _get_ptr(const std::string & name) const {
return dynamic_cast<Cache<T>*>(m_map.at(name).get());
}
public:
template<typename T>
T get(const std::string & name) const {
return _get_ptr<T>(name)->get();
}
template <typename T>
void set(const std::string & name, T t) {
_get_ptr<T>(name)->set(t);
}
template <typename T>
void insert(const std::string & name, T def = 0) {
std::unique_ptr<ICache> up = std::make_unique<Cache<T>>(name);
m_map.insert({name, std::move(up)});
set<T>(name, def);
}
friend std::ostream& operator << (std::ostream & out, const CacheMap & OM) {
out << "OM{";
for (const auto & IO : OM.m_map)
out << IO.first << ": " << *(IO.second.get()) << ", "; // ver1
// out << IO.first << ": " << (IO.second->get()) << ", "; // ver2
out << "}";
return out;
}
};
int main()
{
CacheMap m;
int i= 70000;
m.insert<int>("i", 69999);
m.insert<short>("s", 699);
m.insert<char>("c", 69);
m.set("i", i);
std::cout << m << std::endl;
}
will output on godbolt:
i setting 0 -> 69999
s setting 0 -> 699
c setting 0 -> 69
i setting 69999 -> 70000
OM{c: Cache<c, E>, i: Cache<i, 70000>, s: Cache<s, 699>, }
I just spotted, to protect against very bad and hard to debug errors, I remind you to use dynamic_cast instead of reintepret_cast in CacheMap::_get_ptr().
There are various ways, but generally I would implement a single operator<<() that calls a virtual function.
class ICache {
protected: // so the function is only accessible to derived classes
virtual std::ostream print(std::ostream &out) const = 0; // force derived classes to override
friend std::ostream &operator<<(std::ostream &out, const ICache& c);
};
Then place a single definition of the operator<< in a single compilation unit
// definition of class ICache needs to be visible here
std::ostream &operator<<(std::ostream &out, const ICache& c)
{
return c.print(out);
}
And to implement the derived class
// definition of ICache here
template<class T>
class Cache: ICache
{
protected:
std::ostream print(std::ostream &out) const override
{
// output a Cache<T>
return out;
}
};
The advantage of this is that each class takes responsibility for outputting itself, rather than a container class having to work out which output function to call (and the opportunity for a programmer to forget to do that).
First, reinterpret_cast is a dangerous thing. In contexts of polymorphism you mostly want to use dynamic_cast.
You problem is not about templates etc at all, but it is more about that you want to make the operator<< virtual, which is not possible, since it is a friend function. An easy workaround is the following:
class ICache {
virtual void print(std::ostream &out) const { // Prefer pure virtual
out << "Never print this\n";
}
friend std::ostream &operator<<(std::ostream &out, const ICache& c) {
c.print(out);
return out;
}
};
template<class T>
class Cache: ICache {
void print(std::ostream &out) const override {
out << "Print this instead\n";
}
};
will do what you want without any casting.
I have a problem with my code. I have two classes, A and B, and B inherits A. I also have operators << overloaded in both classes.
Everything works, I have no compiler errors, but it seems something is wrong. As far as I understand polymorphism, when I use pointers to base class while creating child class with new, calling a method should match the child class, not the base class.
For the code below,
#include <iostream>
using namespace std;
class A
{
protected:
int a;
public:
A(int aa) : a(aa) {};
virtual void show(ostream& o) const
{
o << "a = " << a << "\n";
}
};
ostream& operator << (ostream& os, const A &o)
{
o.show(os);
return os;
}
class B : public A
{
private:
int b;
public:
B(int bb, int aa) : A(aa), b(bb){}
int getb() const {return b;}
};
ostream & operator << ( ostream & os, const B & o)
{
os << static_cast <const A &>(o);
os << "\n";
os << "b = " << o.getb() << "\n";
return os;
}
int main()
{
A *o1 = new B(2,3);
cout << *o1;
cout << "---------------------\n";
B *o2 = new B(2,3);
cout << *o2;
return 0;
}
In main:
A *o1 = new B(2,3);
cout << *o1;
Shows a = 3, instead of showing a = 3 b = 2 (the call should match the child class, not the base class). The thing is, I need to implement the << and >> operators in every child class, but I think they do not behave as they should.
The output of the program:
Even the modified code with re-implmented show method shows wrong results, it does not show a at all this time:
#include <iostream>
using namespace std;
class A
{
protected:
int a;
public:
A(int aa) : a(aa) {};
virtual void show(ostream& o) const
{
o << "a = " << a << "\n";
}
};
ostream& operator << (ostream& os, const A &o)
{
o.show(os);
return os;
}
class B : public A
{
private:
int b;
public:
B(int bb, int aa) : A(aa), b(bb) {}
int getb() const
{
return b;
}
void show(ostream& o) const
{
o << "b = " << b << "\n";
}
};
ostream & operator << ( ostream & os, const B & o)
{
os << static_cast <const A &>(o);
o.show(os);
return os;
}
int main()
{
A *o1 = new B(2,3);
cout << *o1;
cout << "---------------------\n";
B *o2 = new B(2,3);
cout << *o2;
return 0;
}
enter image description here
you have to implement the virtual function show in derived class B:
class B: public A
{
public:
// some code here
virtual void show(ostream& o) const
{
o << "b = " << b << "\n";
}
};
when I use pointers to base class while creating child class with new,
calling a method should match the child class, not the base class
It does when you call a member function ("method" in some other languages), but operator<< is not a member function – it's an overloaded free function.
When choosing an overload, only the types known at compile-time are used.
Since o1 is an A*, *o1 is an A&, and the overload for A& is chosen.
You're doing this a bit "backwards"; you only need one operator<<, for the base class, which calls the virtual show, and then you override show in the derived classes.
Like this:
class A
{
// ...
virtual void show(ostream& o) const
{
o << "a = " << a << "\n";
}
};
ostream& operator << (ostream& os, const A &o)
{
o.show(os);
return os;
}
class B : public A
{
// ...
void show(ostream& o) const override
{
A::show(o); // Do the "A part".
o << "b = " << b << "\n";
}
};
Follow the same pattern for operator>>.
Working with a large number of derived classes whose members I would like to display with the << operator. As more classes are used it will likely become quite cumbersome to override the operator/other functions for the new members. I was curious if there was a way to accomplish something similar to the following? Which of course, will overflow.
// Base class
class Foo {
public:
friend ostream& operator <<(ostream& outputStream, const Foo& foo) {
outputStream << foo.fooName + ": " + foo.fooInfo + ", ";
return outputStream;
}
protected:
string fooName;
string fooInfo;
};
// Foo Derived class
class FooBar : public Foo {
public:
friend ostream& operator <<(ostream& outputStream, const FooBar& fooBar) {
outputStream << fooBar; // (fooBar.fooName + ": " + fooBar.fooInfo + ", ")
outputStream << fooBar.barName + ": " + fooBar.barInfo + ", ";
return outputStream;
/* The above as opposed to:
outputStream << fooBar.fooName + ": " + fooBar.fooInfo + ", " <<
fooBar.barName + ": " + fooBar.barInfo + ", "; */
}
protected:
string barName;
string barInfo;
};
If something similar (recursive) is not possible, what is an alternative solution? IE passing different argument(s) or calling an ostream/other function return within the operator body. Thanks.
This may help you out, however the Base class In this case is completely Abstract! Here are the Base & 2 Derived Classes
#include <conio.h>
#include <string>
#include <iostream>
class Base {
friend std::ostream& operator<<( std::ostream& out, const Base* const pObj );
private:
std::string m_strName;
public:
std::string getName() const { return m_strName; }
virtual ~Base(){} // Default Okay
protected:
explicit Base( std::string strName ) : m_strName( strName ) {}
virtual void printData( std::ostream& out ) const = 0;
};
std::ostream& operator<<( std::ostream& out, const Base* const pObj ) {
out << pObj->m_strName << std::endl;
pObj->printData( out );
return out;
}
class DerivedA : public Base {
private:
int m_value;
public:
explicit DerivedA( int& a ) : Base( std::string( "DerivedA" ) ), m_value( a ) {}
protected:
virtual void printData( std::ostream& out ) const override;
};
void DerivedA::printData( std::ostream& out ) const {
out << "Value: " << m_value;
}
class DerivedB : public Base {
private:
float m_value;
public:
explicit DerivedB( float& f ) : Base( std::string( "DerivedB" ) ), m_value( f ) {}
protected:
virtual void printData( std::ostream& out ) const override;
};
void DerivedB::printData( std::ostream& out ) const {
out << "Value: " << m_value;
}
int main () {
int a = 4;
float b = 3.2f;
DerivedA derivedA( a );
DerivedB derivedB( b );
// Notice I Used The Address Of Operator Here Due To How The << operator Is Defined
std::cout << &derivedA << std::endl << std::endl;
std::cout << &derivedB << std::endl << std::endl;
std::cout << "Press any key to quit" << std::endl;
_getch();
return 0;
}
Another way to use this instead of on the stack would be on the heap but make sure to clean up all memory.
int main () {
int a = 4;
float b = 3.2f;
DerivedA derivedA = new DerivedA( a );
DerivedB derivedB = new DerviedB( b );
// Since This Is On The Heap I Can Use The Class Directly
std::cout << derivedA << std::endl << std::endl;
std::cout << derivedB << std::endl << std::endl;
// Make Sure To Clean Up Memory
delete derivedA;
delete derivedB;
std::cout << "Press any key to quit" << std::endl;
_getch();
return 0;
}
What I ended up doing here is I created a friend << operator in the Base Class that will call a printData() function that is defined as being a purely virtual method. Remember that you can not construct a Base object directly since it is completely Abstract. Here each Derived type will have to implement its own printData() function! This way each class will know how to use the << operator since it is defined in the base class that it is inherited from. This is the power of Inheritance. So now I only have to defined the overloaded operator once and can use it for every derived type, but all derived types must specify a printData() function since each derived type may have different internal data that you would like to have printed.
If you do not want to use the pointer notation as I have defined within my Base Class << operator declaration & definition you can do it this way as well:
class Base {
friend std::ostream& operator<<( std::ostream& out, const Base& obj );
// Everything Else Is The Same As Before
};
std::ostream& operator<<( std::ostream& out, const Base& obj ) {
out << obj.m_strName << std::endl;
obj.printData( out );
}
int main() {
// Everything Is Same As Before As In The First main() example
// Except For The Two std::cout Statements Where As Here You
// Can Use The Objects Directly Instead Of Using The Address Of Operator
std::cout << derivedA << std::endl;
std::cout << derivedB << std::endl;
// Rest Is Same As Before.
}
If you want you can include both versions in your base class so that the stream operator << knows how to print your class either by object, reference or pointer!
This code was written by Bruce Eckel in his book "Thinking in C++" Chapter 14 page 649. What I didn't understand was the comment he made below [emphasis added]:
The operator<< for Child is interesting because of the way that it calls the operator<< for the Parent within it : by casting the Child object to a Parent&
(if you cast to a base-class object instead of a reference you will usually get undesirable results).
Here's the corresponding code:
#include <iostream>
using namespace std;
class Parent
{
int i;
public:
Parent(int ii) : i(ii) { cout << "Parent(int ii)\n"; }
Parent(const Parent& b) : i(b.i) { cout << "Parent(const Parent&)\n"; }
Parent() : i(0) { cout << "Parent()\n"; }
friend ostream& operator<<(ostream& os, const Parent& b) {
return os << "Parent: " << b.i << endl;
}
};
class Member
{
int i;
public:
Member(int ii) : i(ii) { cout << "Member(int ii)\n"; }
Member(const Member& m) : i(m.i) { cout << "Member(const Member&)\n"; }
friend ostream& operator<<(ostream& os, const Member& m) {
return os << "Member: " << m.i << endl;
}
};
class Child : public Parent
{
int i;
Member m;
public:
Child(int ii) : Parent(ii), i(ii), m(ii) { cout << "Child(int ii)\n"; }
friend ostream& operator<<(ostream& os, const Child& c) {
return os << (Parent&)c << c.m << "Child: " << c.i << endl;
}
};
int main()
{
Child c(2);
cout << "calling copy-constructor: " << endl;
Child c2 = c;
cout << "values in c2:\n" << c2;
}
That's probably object slicing meant. That is, you'll lose some parts of your object by copying it into an instance of the parent class (instead of assigning a reference).
also have a look at the answers to this question:
What is object slicing?
Here's an example: (see http://ideone.com/qeZoa)
#include <iostream>
using namespace std;
struct parent {
virtual const char* hi() const { return "I'm your father..."; }
};
struct child : public parent {
const char* hi() const { return "No way!"; }
};
int main() {
child c;
cout << ((parent) c).hi() << endl;
cout << ((parent&)c).hi() << endl;
}
Yes this is okay . Parent and Child are Polymorphic. as Child inherit's Client you can cast a Child& to Parent&
In expression os << (Parent&)c rhs (Parent&)c is of type Parent& So the operator<<(ostream& os, const Parent& b) will be called from operator<<(ostream& os, const Child& b)
In C++ Polymorphism only works when you have reference or Pointer type
The undesirable result is that the compiler has to create a temporary copy of the Parent portion of the Child object, pass a reference to that temporary to the inserter, and then destroy the temporary. That's a lot of churning...
And, as #NeelBasu hinted, if Parent has virtual functions that are overridden in Child, calls to those functions from the inserter won't call the Child version, because the object passed in is a Parent object.