例6—1运算符重载的方法
/*
p155
例6—1运算符重载的方法
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Complex
{
public:
Complex() { real = 0; imag = 0; }
Complex(double r, double i) { real = r; imag = i; }
friend Complex operator+ (Complex &a, Complex &b);
void display();
private:
double real;
double imag;
};
Complex operator+ (Complex &a, Complex &b)
{
return Complex(a.real + b.real, a.imag + b.imag);
}
void Complex::display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{
Complex c1(3, 4), c2(5, -10), c3;
c3 = c1 + c2;
cout << "c1="; c1.display();
cout << "c2="; c2.display();
cout << "c3="; c3.display();
//getchar(); /*防止程序运行结束之后窗口自动关闭*/
return 0;
}`
例6—2运算符重载为成员函数和友元函数
/*
p158
例6—2运算符重载为成员函数和友元函数
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Complex
{
public:
Complex() { real = 0; imag = 0; }
Complex(double r, double i) { real = r; imag = i; }
// friend Complex operator+ (Complex &a, Complex &b);
Complex operator+ (Complex &b);
void display();
private:
double real;
double imag;
};
//Complex operator+ (Complex &a, Complex &b)
//{return Complex(a.real + b.real, a.imag + b.imag);}
Complex Complex::operator+(Complex &b)
{
return Complex(real + b.real, imag + b.imag);
}
void Complex::display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{
Complex c1(3, 4), c2(5, -10), c3;
//c3 = c1 + c2;
c3=c1.operator+(c2);
cout << "c1="; c1.display();
cout << "c2="; c2.display();
cout << "c1+c2="; (c1.operator+(c2)).display(); /*或者 “c3.display();”*/
//getchar(); /*防止程序运行结束之后窗口自动关闭*/
return 0;
}
/*
1、重载运算符的函数不能有默认参数,否则就改变了运算符参数的个数 有的运算符可以是单目运算符,可以是双目运算符
假如单目运算符有一个参数,双目运算符后面的第二个参数是默认参数,那么对象的函数赋值时提供一个参数,不知道是赋给单目运算符,还是双目运算符
2、用户定义的类型都自动拥有=、&、“,”运算符,除非有特殊需要,一般不必重载这三个运算符
3、有时系统提供的默认的对象赋值运算符不能满足程序的要求,如数据成员中包含 指向动态分配内存的指针成员 时,再复制此成员就可能出现危险,在这种情况下,就需要自己重载赋值运算符
4、运算符可以重载为类的成员函数(总是通过该类的某个对象来访问重载运算符)和类的非成员函数(例如友元函数,可以访问运算符对象的私有成员)。
5、运算符重载为类成员函数时:参数个数=原操作个数-1 (后置++、–除外 ????????)
假如双目运算符: 一个函数 objector1 B objector2 相当于 成员函数 objector1.operator B(objector2)和友元函数operator(objector1,operator2)
由于可以通过this指针自由访问本类的数据成员,所以可以少写一个函数的参数,但必须要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,因为必须通过类的对象去调用该类的成员函数
6、后置单目运算符: 如++和–,如果要重载他们成为类成员函数,使之能够实现oprd++或者opre–,其中oprd为A类对象,则++或者–应被重载为A类的成员函数,且具有一个int类型形参,用来区别前置单目运算符。
重载后,表达式oprd++相当于oprd.operator++(0)
如果运算符重载为友元函数,oprd++等同于operator++(oprd,0);
7、C++规定,当重载以下的运算符时,必须重载为某个类的成员函数:
=、[]、()、->
/当重载以下运算符时,必须是普通函数或者友元函数,不能为成员函数:
>>、<<
一般将双目运算符重载为友元函数,单目运算符重载为成员函数
由于友元函数的 使用会破坏类的封装性,因此从原则上说,要尽量将运算符函数作为成员函数。
*/
例6—3重载双目运算符##
/*
p159
例6—3重载双目运算符
*/
#include "stdafx.h"
#include<iostream>
#include "string"
using namespace std;
class String
{
public:
String() { p = NULL; }
String(const char *str); /* 注意在C++中,定义字符串指针,应为const char *str,而不是char *str */
void display();
friend bool operator>(String &string1, String &string2);
private:
const char *p;
};
String::String(const char *str)
{
p = str;
}
void String::display()
{
cout << p;
}
bool operator>(String &string1, String &string2)
{
if (strcmp(string1.p, string2.p) > 0)
return true;
else return false;
}
int main()
{
String string1("Hello"), string2("Book");
#ifdef hu
string1.display();
cout << endl;
string2.display();
#endif
cout << (string1 > string2) << endl; /*结果输出1*/
getchar();
return 0;
}
例6— 4重载单目运算符##
/*
p161
例6—4重载单目运算符
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Time
{
public:
Time() { minute = 0; sec = 0; }
Time(int m, int s) :minute(m), sec(s){}
Time operator++();
void display() { cout << minute << ":" << sec << endl; }
private:
int minute;
int sec;
};
Time Time::operator++()
{
if (++sec >= 60)
{
sec = sec - 60;
++minute;
}
return *this;
}
int main()
{
Time time(34, 8);
for (int i = 0; i < 61; i++)
{
++time;
time.display();
}
getchar();
return 0;
}
/函数作用是执行对象 ++time一次,然后对象time的两个参数中的一个加一,并通过成员函数显示出来/
例6— 5后置重载运算符的重载##
/*
p162
例6—5后置重载运算符的重载
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Time
{
public:
Time() { minute = 0; sec = 0; }
Time(int m, int s) :minute(m), sec(s) {}
Time operator++();
Time operator++(int);
void display() { cout << minute << ":" << sec << endl; }
private:
int minute;
int sec;
};
Time Time::operator++()
{
if (++sec >= 60)
{
sec = sec - 60;
++minute;
}
return *this;
}
Time Time::operator++(int) /*带有参数,返回的是自加前的对象*/
{
Time temp(*this); /*看一下,与 前面那个不同 ,(*this) 去掉无影响,只是为了说明,此时对象的指针指向的是临时变量 */
sec++; /* 先不加,如果大于60,对象就++功能,minute也+1 */
if (sec >= 60)
{
sec -= 60;
++minute;
}
return temp;
//return *this;
}
int main()
{
Time time1(34, 8), time2;
cout << "Time1:";
time1.display();
++time1;
cout << "++time1:";
time1.display();
time2 = time1++;
cout << "time1++:";
time1.display();
cout << "time2:";
time2.display();
return 0;
}
/函数作用是执行对象 ++time一次,然后对象time的两个参数中的一个加一,并通过成员函数显示出来/
例6— 6友元函数重载流插入运算符<<和流提取运算符>>##
/*
p163
例6—6友元函数重载流插入运算符<<和流提取运算符>>
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Complex
{
public:
friend ostream& operator<<(ostream &, Complex &);
friend istream& operator>>(istream &, Complex &);
// friend istream& operator(istream& cin,int &1);里面的1可以是一个字符a,或者是一个浮点数1.58,它是类 istream& 的对象,这个对象可以是int char float等基本类型的兼容变量
private:
double real;
double imag;
};
ostream& operator<<(ostream& output, Complex &c)
{
output << "(" << c.real;
if (c.imag >= 0)output << "+";
output << c.imag << "i)" << endl;
return output;
}
istream& operator>>(istream& input, Complex &c)
/*
假设operator作用是连接,就是为了使 >> 后面的对象接着输入
第一个参数类型必须是istream&,后面input是cin的引用,代表cin的原始功能,只能从输入流提取 istream& 基本类型的变量,
第二个参数是从输入流提取的 目的输入类对象 的引用 */
{
cout << "input real part and imaginary part of complex number:";
input >> c.real >> c.imag;
return input; /* 函数返回cin的新值input,用 cin和>> 可以连续向程序输入Complex 类对象的值,把它想成流
返回新值input,就是旧版的cin, 那么后面还需要输入>>,以便继续输入下一个对象,如果输入分号,那么程序结束 所以要看输入对象后接下来的输入是>>继续输入,是分号结束输入*/
}
int main()
{
Complex c1, c2;
cin >> c1 >> c2;
cout << "c1=" << c1 << endl;
cout << "c2=" << c2 << endl;
/*以下几行是为了程序运行结束时不自动关闭窗口
cin.clear()可以理解为清除的意思。当cin的状态被设置为错误状态了,调用cin.clear(istream::failbit)可以清除这个failbit(输入失败)状态复位到初始状态。
一般来说程序中多用if(!cin){cout<<"error";}这样类似的句式来检查输入流,用于保证输入的无误。*/
cin.clear();
char ch;
cin >> ch;
return 0;
}
/*
在运算符重载中使用引用的重要性,利用引用作为函数的形参可以在调用函数的过程中不是用值传递的方式进行虚实结合,而是通过使形参成为实参的别名,减少了时间和空间的开销。
此外,如果重载函数的返回值使对象的引用时,返回的不是常量,而是引用所代表的对象,他可以出现在赋值号的左侧成为左值,可以被赋值或参与其他操作(如保留cout流的当前值以便能连续使用“<<”进行输出。
但是使用引用时要特别小心,因为修改了引用就等于修改了它所代表的对象
*/
例6— 8基类与派生类中同名函数设为虚函数##
/*
p169
例6—8基类与派生类中同名函数设为虚函数
*/
#include "stdafx.h"
#include "string"
#include<iostream>
using namespace std;
class Student
{
public:
Student(int, string, float);
virtual void display();
protected:
int num;
string name;
float score;
};
Student::Student(int n, string nam, float s)
{
num = n;
name = nam;
score = s;
}
void Student::display()
{
cout << "num:" << num << "\nname:" << name << "\nscore" << score << "\n\n";
}
class Graduate :public Student
{
public:
Graduate(int, string, float, float);
virtual void display();
private:
float pay;
};
void Graduate::display()
{
cout << "num:" << num << "\nname:" << name << "\nscore:" << score << "\npay:" << pay << endl;
}
Graduate::Graduate(int n,string nam,float s,float p):Student(n,nam,s),pay(p){}
int main()
{
Student stud1(1001, "li", 87.5);
Graduate grad1(200, "wang", 98.5, 563.5);
Student *pt = &stud1; /*pt一个基类指针,可以调用同一类族中不同类的虚构函数,这就是多态性*/
pt->display();
pt = &grad1;
pt->display();
getchar();
return 0;
}
例6— 9理解虚函数##
/*
p171
例6—9理解虚函数
*/
/*
class A {
int const func();
int func() const;
};
int const A::func() { return 0; }
int A::func() const { return 0; }
上面的代码是合法的,其中A::func成员函数是一个重载成员函数,两个函数都返回int类型数据(注意:对于C/C++,返回类型或参数类型中,const int和int被认为是
一种类型。但是const int *和int *不是一种类型),这两个重载函数正是基于函数名后的const来重载的。
int const func();表示该成员函数的隐藏this指针参数是A * const类型的;而int func() const;表示该重载成员函数的隐藏this指针参数是A const * const类型的.
A * const类型和A const * const类型是不同类型,因此可以重载。
由此可见const放在函数名后和名前是不同的
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class B
{
public:
virtual void f()const { cout << "B::f "; }
void g() const { cout << "B::g "; }
};
class D :public B
{
public:
void f() const { cout << "D::f "; } //重写B::f函数
void g() { cout << "D:g "; }
};
class DD :public D
{
public:
void f() { cout << "DD::f "; } /*此处没有const,不是D:f的重写函数*/
void g() const { cout << "DD::g "; }
};
void call(const B &b)
{
b.f();
b.g();
}
int main()
{
B b;
D d;
DD dd;
call(b);
call(d);
call(dd);
b.f();
b.g();
d.f();
d.g();
dd.f();
dd.g();
return 0;
}
/*运行结果:
B::f B::g D::f B::g D::f B::g B::f B::g D::f D : g DD::f DD::g
*/
例6—10基类指针指向派生类对象,基类有非虚析构函数##
/*
p173
例6—10基类指针指向派生类对象,基类有非虚析构函数
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Point
{
public:
Point() {}
~Point() { cout << "executing Point destructor" << endl; }
};
class Circle :public Point
{
public:
Circle(){}
~Circle() { cout << "executing Circle destructor" << endl; }
private:
int radius;
};
int main()
{
Point *p = new Circle; /* 指向基类的指针变量,指向new 开辟的派生类Circle对象创立的动态存储空间 */
delete p;
getchar();
return 0;
}
/*
运行结果:
executing Point destructor
*/
例6—11基类指针指向派生类对象,基类有虚析构函数##
/*
p174
例6—11基类指针指向派生类对象,基类有虚析构函数
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Point
{
public:
Point() {}
virtual ~Point() { cout << "executing Point destructor" << endl; }
};
class Circle :public Point
{
public:
Circle(){}
~Circle() { cout << "executing Circle destructor" << endl; }
private:
int radius;
};
int main()
{
Point *p = new Circle; /* 指向基类的指针变量,指向new 开辟的派生类Circle对象创立的动态存储空间 */
delete p;
getchar();
return 0;
}
/*
运行结果:
executing Circle destructor
executing Point destructor
*/
/*
基类的析构函数声明为虚函数时,由该基类派生的所有派生类的析构函数也自动成为析构函数,即使派生类的析构函数与基类的析构函数名字不相同。
构造函数不能声明为虚函数,这是因为在执行构造函数时对象还未完成建立过程,当然谈不上对象与函数的绑定
如果程序中的局部对象离开其作用域,系统会隐式地调用其析构函数
*/
例6—12析构函数地隐式调用和显式调用##
/*
p174
例6—12析构函数地隐式调用和显式调用
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Point
{
public:
Point() {}
virtual ~Point() { cout << "executing Point destructor" << endl; }
};
class Circle :public Point
{
public:
Circle() {}
~Circle() { cout << "executing Circle destructor" << endl; }
private:
int radius;
};
Point *fc()
{
Circle c1;
Point *p = new Circle;
return p;
}
int main()
{
Point *q = fc(); /* 指向基类的指针变量,指向new 开辟的派生类Circle对象创立的动态存储空间 */
delete q;
}
/*运行结果:
executing Circle destructor
executing Point destructor
executing Circle destructor
executing Point destructor
*/
/*
析构函数显式调用Circle
再隐式调用Point
*/
例6—13虚函数和抽象类##
/*
p176
例6—13虚函数和抽象类
*/
#include "stdafx.h"
#include<iostream>
using namespace std;
class Shape
{
public:
virtual double area() = 0; /*将一个函数声明为纯虚函数,需要在原型语句符;之前加上=0*/
virtual float volume()const { return 0; }
virtual void shapeName()const = 0;
};
class Point :public Shape
{
public:
Point(float = 0, float = 0);
void setPoint( float,float );
float getX() const { return x; }
float getY() const { return y; }
virtual double area() { return 0; };
virtual float volume()const { return 0; }
virtual void shapeName() const { cout << "Point:"; }
friend ostream &operator<<(ostream &, const Point &);
protected:
float x, y;
};
Point::Point(float a, float b)
{
x = a; y = b;
}
//void Point::setPoint(float a, float b)
//{
// x = a; y = b;
//}
ostream &operator<<(ostream &output, const Point &p)
{
output << "[" << p.x << "," << p.y << "]";
return output;
}
class Circle :public Point
{
public:
Circle(float x = 0, float y = 0, float r = 0);
void setRadius(float);
float getRadius()const;
virtual double area();
virtual void shapeName()const;
friend ostream &operator<<(ostream&, const Circle &);
protected:
float radius;
};
Circle::Circle(float a, float b, float r) :Point(a, b), radius(r) {}
void Circle::setRadius(float r) { radius = r; }
float Circle::getRadius() const { return radius; }
double Circle::area() { return 3*radius*radius; }
void Circle::shapeName()const {
cout << "Circle" ;
}
ostream &operator<<(ostream &output, const Circle &c) /*能输出一个类圆的圆心和半径*/
{
output << "[" << c.x << "," << c.y << "],r=" << c.radius;
return output;
}
int main()
{
Point point(3 , 4);
Circle circle(2, 1, 6);
point.shapeName();
cout << point << endl;
circle.shapeName();
cout << circle << endl<<endl;
Shape *pt; /*通过动态关联在运行时将指针与所调用的函数关联起来*/
pt = &point;
pt->shapeName();
cout << "x=" << point.getX() << ", y=" << point.getY() << "\narea=" << pt->area() << "\nvolume=" << pt->volume() << "\n\n";
pt = &circle;
pt->shapeName();
cout << "x=" << circle.getX() << ", y=" << circle.getY() << "\narea" << pt->area() << "\nvolume=" << pt->volume() << "\n";
return 0;
}
/*运行结果:
Point:[3,4]
Circle[2,1],r=6
Point:x=3, y=4
area=0
volume=0
Circlex=2, y=1
area108
volume=0
*/
/*
抽象类只能作为其他类的基类,不能建立抽象类的对象。抽象类属于继承层次的较上层,,一个抽象类自身无法实例化只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化
抽象类不能用作参数类型、函数返回值、或者显式转换的类型。(涉及到强制转化,区别与转换构造函数)
抽象类不能定义对象,但是可以声明一个抽象类的指针和引用。通过指针和引用可以指向并访问派生类的对象,以访问派生类的成员。
重点:::抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以声明自己的对象,因而不再是抽象类;
反之,如果派生类没有给出全部虚函数的实现,这时的派生类仍然是一个抽象类
*/