可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've been trying to find an answer to this, but no one seems to have exactly the same problem as I do.
I am working with several derived classes. The ostream operator << for each of these should print out some things common to each, and some things specific to each. Later on, I would like to further derive from these derived classes, and again the new derived classes need to print out some things that are in the "generations" above them.
For example:
The Base class .h file
class Base
{
int FirstClassNumber;
//The declaration I'm currently working with, that a friend gave me
//I'm pretty sure my problem lies here.
public:
friend ostream& operator << (ostream& os, const Base &base)
{
base << os ;
return os;
}
virtual void operator << (ostream& os) const = 0;
};
The Base.cpp file includes these lines:
void Base::operator << (ostream& os)
{
os << FirstClassNumber;
}
Then I derive: (FirstDerived.h)
class FirstDerived : Public Base
{
int SecondClassNumber;
};
FirstDerived.cpp:
FirstDerived::operator << (ostream& os)
{
os <<
"The first Number is:
//This is the line that isn't working - someone else gave me this syntax
<< Base::operator<<
<< "The second number is"
<< SecondClassNumber;
}
Then I want to derive:
class SecondDerived: Public FirstDerived
{
int ThirdClassNumber;
};
Second.cpp:
FirstDerived::operator << (ostream& os)
{
os <<
FirstDerived::operator<<
<< "The third number is "
<< ThirdClassNumber;
}
I think the problem is most likely either the declaration in the very start of the program, or the lines like Base::operator<<
.
Another possibility is that I'm not redeclaring it in the .h file of every inherited class. Should I be, and if so what syntax should I use?
It was suggested to me to use the static_cast
method, but my professor (the one who wrote the assignment, and therefore won't give us too much help with it) said that there's a better way to do it. Any suggestions?
回答1:
A simple technique for this is:
class Base
{
int FirstClassNumber;
public:
virtual void serialize(ostream& os) const
{
os << FirstClassNumber;
}
};
// Implement the stream operator for the base class.
// All it does is call erialize which is a virtual method that
// will call the most derived version.
ostream& operator << (ostream& os, const Base &base)
{
base.serialize(os);
return os;
}
class FirstDerived:public Base
{
int SecondClassNumber;
public:
// Override serialize to make it call the base version.
// Then output any local data.
virtual void serialize(ostream& os) const
{
Base::serialize(os);
os << SecondClassNumber;
}
};
回答2:
You cannot implement operator<< for ostreams as a class member - it has to be a free (possibly friend) function. This is because in the expression:
os << x;
the thing on the left-hand side of the << will not be an instance of your class, which it would have to be if it were a member function.
To call the parent from the child - do a static_cast:
ostream & operator << ( ostream & os, const Child & c ) {
os << static_cast <const Parent &>( c );
// child stuff here
}
which I think is the "best" solution. Alternatively, give your classes a a named function call Print() which takes an ostream as a parameter and use this to implement your operator<<. This will result in much cleaer code.
回答3:
Aside from what @Neil says, it would probably be better to implement a virtual DoStream
method, so you don't need the upcasting:
class Base{
private:
virtual void DoStream(ostream& os){
// general stuff
}
public:
friend ostream& operator<<(ostream& os, Base& b){
b.DoStream(os);
return os;
}
};
class Derived : public Base{
private:
void DoStream(ostream& os){
Base::DoStream(os);
// derived specific stuff
}
};
So you only need to implement the operator once. You could also make the operator<<
non-friend and the DoStream
public, but that is probably personal preference.
回答4:
FirstDerived.cpp:
FirstDerived::operator << (ostream& os)
{
os << "The first Number is:"
//This is the line that isn't working - someone else gave me this syntax
<< Base::operator<<
<< "The second number is"
<< SecondClassNumber;
}
You need to call the function by putting parenthesis after it, and provide the argument it expects. It doesn't have a return value so shouldn't be in the set of things being streamed. Summarily:
os << "The first number is: "; // finish streaming statement with ";"
Base::operator<<(os); // separate statement to call this function...
os << "The second number is " << SecondClassNumber; // start streaming again
回答5:
To call a method from base class you can use:
Base::method(/*parameters*/)
But operator<<
is a free function. The only possibility without static_cast I can see is to define the operator as a template and then explicitly call the specialization like this:
template<typename T>
void function(T const &t);
template<>
void function<Base>(Base const &t) {
// own implementation ...
}
template<>
void function<Derived>(Derived const &t) {
function<Base>(t);
// own implementation ...
}
This could be done the same way for operator<<, just change the name function
for operator<<
and add required parameters.
Another possibility is to make a virtual member function:
class Base {
virtual void print(ostream &os) const {
// print self
}
};
In Derived class this could call the Base print function:
class Derived {
virtual void print(ostream &os) const {
Base::print(os);
// print self
}
};
Then it is enough to have operator<<
only for the Base
class and it will call appropriate print method polymorphicaly. Note that the operator<<
is a free function.
ostream &operator<< (ostream &os, Base const &b) {
b.print(os);
return os;
}
回答6:
Here's a slight modification to Loki's answer. Instead of having a virtual serialize method, I use a virtual to_string method. I think this can be reused in more contexts outputing to an ostream, which might be of use.
#include <iostream>
#include <string>
class Base
{
public:
Base();
virtual std::string to_string() const;
protected:
int first_class_number;
friend std::ostream& operator<<(std::ostream& os, const Base &base);
};
Base::Base()
: first_class_number(1)
{
}
std::string Base::to_string() const
{
return "Base: "+std::to_string(first_class_number);
}
class FirstDerived : public Base
{
public:
FirstDerived();
std::string to_string() const;
protected:
int second_class_number;
};
FirstDerived::FirstDerived()
: second_class_number(2)
{
}
std::string FirstDerived::to_string() const
{
return "FirstDerived: "+std::to_string(first_class_number)+" "+ std::to_string(second_class_number);
}
std::ostream& operator << (std::ostream& os, const Base &base)
{
os << base.to_string();
return os;
}
int main(int argc, const char *argv[])
{
std::cout << Base() << std::endl;
std::cout << FirstDerived() << std::endl;
return 0;
}
Produces
Base: 1
FirstDerived: 1 2