对于Complex类,最初觉得很容易。写下来之后,对照着大师的评论和实现,发现了差距,也理清了思路。在此贴出,以做参照。
/**/
/************************************************************************
The standard requires that operators = () [] and -> must be members, and
class-specific operators new, new[], delete, and delete[] must be static members.
For all other functions:
if the function is operator>> or operator<< for stream I/O,
or if it needs type conversions on its leftmost argument,
or if it can be implemented using the class's public interface alone,
make it a nonmember (and friend if needed in the first two cases)
if it needs to behave virtually,
add a virtual member function to provide the virtual behavior,
and implement it in terms of that
else
make it a member
************************************************************************/
#include < iostream >
using namespace std;
template < typename T >
class Complex
... {
template <typename Ty> friend class Complex;
public:
// In general it's a good idea to make your constructors explicit by default
// unless you deliberately decide to allow the implicit conversion
explicit Complex(T real, T imag = T()) : real_(real), imag_(imag)
...{
}
// Prefer writing "a op= b;" instead of "a = a op b;" (where op stands for any operator).
// It's clearer, and it's often more efficient
template <typename Ty>
Complex& operator +=(const Complex<Ty>& other)
...{
real_ += other.real_;
imag_ += other.imag_;
return *this;
}
template <typename Ty>
Complex& operator -=(const Complex<Ty>& other)
...{
real_ -= other.real_;
imag_ -= other.imag_;
return *this;
}
// Preincrement should return a reference to non-const梚n this case, Complex&.
// This lets client code operate more intuitively and avoids needless inefficiency.
Complex& operator ++()
...{
++real_;
return *this;
}
Complex& operator --()
...{
--real_;
return *this;
}
// Postincrement should return a const value in this case, const Complex.
// By not allowing changes to the returned object, we prevent questionable code like "a++++",
// which doesn't do what a naïve user might think it does.
// For consistency, always implement postincrement in terms of preincrement.
// Otherwise, your users will get surprising (and often unpleasant) results
const Complex operator ++(int)
...{
Complex temp(*this);
++*this;
return temp;
}
const Complex operator --(int)
...{
Complex temp(*this);
--*this;
return temp;
}
// Add a helper method to avoid friend methods for operator <<
// and if it is a base class, add virtual to print derived class
ostream& print(ostream& os) const
...{
return os << "(" << real_ << ", " << imag_ << ")";
}
private:
// follow the convention of designating member variable names with a trailing underscore
T real_, imag_;
} ;
// Notice the relationship between operators + and +=, - and -=.
// The former should be implemented in terms of the latter,
// both for simplicity (the code is easier to write) and
// for consistency (the two will do the same thing and
// are less likely to diverge during maintenance).
template < class T1, class T2 >
const Complex < T1 > operator + ( const Complex < T1 >& lhs, const Complex < T2 >& rhs)
... {
Complex<T1> ret(lhs);
ret += rhs;
return ret;
}
template < class T1, class T2 >
const Complex < T1 > operator - ( const Complex < T1 >& lhs, const Complex < T2 >& rhs)
... {
Complex<T1> ret(lhs);
ret -= rhs;
return ret;
}
// operator<< should not be a member function.
// operator<< should have a return type of "ostream&" and
// should return a reference to the stream in order to permit chaining.
// That way, users can use your operator<< naturally in code like "cout << a << b;".
template < class T >
ostream & operator << (ostream & os, const Complex < T >& com)
... {
return com.print(os);
}
int main()
... {
Complex<double> c2(3, 4);
Complex<double> c3(0.1, 2.5);
cout << c2 << endl
<< c3 << endl
<< c3 + c2 << endl
<< c3 - c2 << endl;
return getchar();
}
The standard requires that operators = () [] and -> must be members, and
class-specific operators new, new[], delete, and delete[] must be static members.
For all other functions:
if the function is operator>> or operator<< for stream I/O,
or if it needs type conversions on its leftmost argument,
or if it can be implemented using the class's public interface alone,
make it a nonmember (and friend if needed in the first two cases)
if it needs to behave virtually,
add a virtual member function to provide the virtual behavior,
and implement it in terms of that
else
make it a member
************************************************************************/
#include < iostream >
using namespace std;
template < typename T >
class Complex
... {
template <typename Ty> friend class Complex;
public:
// In general it's a good idea to make your constructors explicit by default
// unless you deliberately decide to allow the implicit conversion
explicit Complex(T real, T imag = T()) : real_(real), imag_(imag)
...{
}
// Prefer writing "a op= b;" instead of "a = a op b;" (where op stands for any operator).
// It's clearer, and it's often more efficient
template <typename Ty>
Complex& operator +=(const Complex<Ty>& other)
...{
real_ += other.real_;
imag_ += other.imag_;
return *this;
}
template <typename Ty>
Complex& operator -=(const Complex<Ty>& other)
...{
real_ -= other.real_;
imag_ -= other.imag_;
return *this;
}
// Preincrement should return a reference to non-const梚n this case, Complex&.
// This lets client code operate more intuitively and avoids needless inefficiency.
Complex& operator ++()
...{
++real_;
return *this;
}
Complex& operator --()
...{
--real_;
return *this;
}
// Postincrement should return a const value in this case, const Complex.
// By not allowing changes to the returned object, we prevent questionable code like "a++++",
// which doesn't do what a naïve user might think it does.
// For consistency, always implement postincrement in terms of preincrement.
// Otherwise, your users will get surprising (and often unpleasant) results
const Complex operator ++(int)
...{
Complex temp(*this);
++*this;
return temp;
}
const Complex operator --(int)
...{
Complex temp(*this);
--*this;
return temp;
}
// Add a helper method to avoid friend methods for operator <<
// and if it is a base class, add virtual to print derived class
ostream& print(ostream& os) const
...{
return os << "(" << real_ << ", " << imag_ << ")";
}
private:
// follow the convention of designating member variable names with a trailing underscore
T real_, imag_;
} ;
// Notice the relationship between operators + and +=, - and -=.
// The former should be implemented in terms of the latter,
// both for simplicity (the code is easier to write) and
// for consistency (the two will do the same thing and
// are less likely to diverge during maintenance).
template < class T1, class T2 >
const Complex < T1 > operator + ( const Complex < T1 >& lhs, const Complex < T2 >& rhs)
... {
Complex<T1> ret(lhs);
ret += rhs;
return ret;
}
template < class T1, class T2 >
const Complex < T1 > operator - ( const Complex < T1 >& lhs, const Complex < T2 >& rhs)
... {
Complex<T1> ret(lhs);
ret -= rhs;
return ret;
}
// operator<< should not be a member function.
// operator<< should have a return type of "ostream&" and
// should return a reference to the stream in order to permit chaining.
// That way, users can use your operator<< naturally in code like "cout << a << b;".
template < class T >
ostream & operator << (ostream & os, const Complex < T >& com)
... {
return com.print(os);
}
int main()
... {
Complex<double> c2(3, 4);
Complex<double> c3(0.1, 2.5);
cout << c2 << endl
<< c3 << endl
<< c3 + c2 << endl
<< c3 - c2 << endl;
return getchar();
}