练习:
实现3*3矩阵类,支持如下操作符:
+ - += -=
#include<iostream>
#include<iomanip>//setw()设置域宽
using namespace std;
class M33{
public:
M33(void){
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
m_a[i][j]=0;
}
M33(int a[][3]){
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
m_a[i][j]=a[i][j];
}
friend ostream& operator<<(ostream& os,const M33& m){
for(int i=0;i<3;i++){
for(int j=0;j<3;j++)
os<<setw(4)<<m.m_a[i][j];
os<<endl;
}
return os;
}
const M33 operator+(const M33& m)const{
int a[3][3]={};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
a[i][j]=m_a[i][j]+m.m_a[i][j];
return a; //return M33(a);
}
const M33 operator-(const M33& m)const{
int a[3][3]={};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
a[i][j]=m_a[i][j]-m.m_a[i][j];
return a; //return M33(a);
}
const M33 operator*(const M33& m)const{
int a[3][3]={};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
a[i][j]+=m_a[i][k]*m.m_a[k][j];
return a; //return M33(a);
}
//+ - *返回值右值,左操作数必须是左值,右操作数可以为左值,也可以为右值
M33& operator+=(const M33& m){
return *this=*this + m;
}
M33& operator-=(const M33& m){
return *this=*this - m;
}
M33& operator*=(const M33& m){
return *this=*this * m;
}
private:
int m_a[3][3];
};
int main(){
int a1[3][3]={1,2,3,4,5,6,7,8,9};
int a2[3][3]={9,8,7,6,5,4,3,2,1};
M33 m1(a1);
M33 m2(a2);
cout<<m1<<endl;
cout<<m2<<endl;
cout<<"m1+m2"<<endl;
cout<<m1+m2<<endl;
cout<<"m1-m2"<<endl;
cout<<m1-m2<<endl;
cout<<"m1*m2"<<endl;
cout<<m1*m2<<endl;
cout<<"m1+=m2"<<endl;
cout<<(m1+=m2)<<endl;
cout<<"m1-=m2"<<endl;
cout<<(m1-=m2)<<endl;
cout<<"m1*=m2"<<endl;
cout<<(m1*=m2)<<endl;
return 0;
}
tarena@tarena-virtual-machine:~/day42$./a.out
1 2 3
4 5 6
7 8 9
9 8 7
6 5 4
3 2 1
m1+m2
10 10 10
10 10 10
10 10 10
m1-m2
-8 -6 -4
-2 0 2
4 6 8
m1*m2
30 24 18
84 69 54
138114 90
m1+=m2
10 10 10
10 10 10
10 10 10
m1-=m2
1 2 3
4 5 6
7 8 9
m1*=m2
30 24 18
84 69 54
138114 90
3、(操作符重载,接昨天)单目操作符重载:#O
3.1 计算类的单目操作符(~ 、 !…) !a, ~a ; -a; &a ;
1)成员函数形式
#O==》O.operator#( )
2)全局函数形式
#O==》::operator#(0)
举例:
1#include<iostream>
2using namespace std;
3class Integer{
4public:
5 Integer(int i=0):m_i(i){}
6 const Integeroperator-(void)const{//取负操作符重载
7 return Integer(-m_i);
8 }
9 //自定义~表示乘方
10 friend const Integer operator~(const Integer& i){
11 return Integer(i.m_i*i.m_i);
12 }
13 friend ostream& operator<<(ostream& os,const Integer&i){
14 os<<i.m_i;
15 }
16private:
17 int m_i;
18};
19int main(void){
20 Integer i(100);
21 Integer j=-i;
22 cout<<i<<endl;//-100
23 cout<<j<<endl;//-100
24 j=~i;
25 cout<<j<<endl;//10000
26 return 0;
27 }
3.2自增减操作符
1)前缀自增减
成员函数: #O==》O.operator#( )
全局函数: #O==》::operator#(0)
++n; 操作数必须是左值,返回值就是操作数自身也是左值
2)后缀自增减
成员函数: #O==》O.operator#( int)
全局函数: #O==》::operator#(0,int)
int num=10;
num++;
num ; 11
操作数必须是左值,返回值是右值,是自增减之前的副本
举例:
1#include<iostream>
2using namespace std;
3class Integer{
4public:
5 Integer(int i=0):m_i(i){}
6 //前++ (可以用友元) 成员函数
7 Integer&operator++(void){
8 ++m_i;
9 return *this;
10 }
11 //前-- 全局函数
12 friend Integer& operator--(Integer& i){
13 --i.m_i;
14 return i;
15 }
16 //后++ (可以用友元friend,这样也可以)
17 const Integer operator++(int){
18 Integer old=*this;
19 ++*this;//复用前++
20 return old;
21 }
22 //后--
23 friend const Integeroperator--(Integer& i,int){
24 Integer old=i;
25 --i;
26 return old;
27 }
28 friend ostream& operator<<(ostream& os,const Integer&i){
29 os<<i.m_i;
30 }
31private:
32 int m_i;
33};
34int main(void){
35 Integer i(123);
36 Integer j=++i;
37 cout<<"i="<<i<<endl;//124
38 cout<<"j="<<j<<endl;//124
39 j=++++++i;
40 cout<<"i="<<i<<endl;//127
41 cout<<"j="<<j<<endl;//127
42
43 j=i++;
44 cout<<"i="<<i<<endl;//128
45 cout<<"j="<<j<<endl;//127
46
47 j=--i;
48 cout<<"i="<<i<<endl;//127
49 cout<<"j="<<j<<endl;//127
50
51 j=i--;
52 cout<<"i="<<i<<endl;//126
53 cout<<"j="<<j<<endl;//127
54 return 0;
55 }
4、函数操作符:()(少用!)
如果一个类重载了函数操作符,那么该类的对象就可以当做函数来调用,其参数和返回值就是函数操作符函数的参数和返回值
1#include<iostream>
2using namespace std;
3class Square{
4public:
5 double operator()(doublex)const{
6 return x*x;
7 }
8 int operator()(int a,intb,int c=9){
9 return a+b-c;
10 }
11};
12int main(void){
13 Square square;
14 cout<<square(13.)<<endl;//square.operator()(13.)
15 cout<<square(10,40,20)<<endl;//30
16 cout<<square(10,40)<<endl;//41
17 return 0;
18 }
5、下标操作符,[]
int arr[5]={1,2,3,4,5};
arr[1]=20;
cosnt int (rarr)[5]= arr;
rarr[1]=20;//error
举例:
1#include<iostream>
2using namespace std;
3class Array{
4public:
5 Array(int size):m_data(newint[size]),m_size(size){}
6 ~Array(void){
7 delete[] m_data;
8 m_data=0;
9 }
10 int& operator[](int i){//非常对象调用
11 return m_data[i];
12 }
13 const int& operator[](int i)const{//常对象调用
14 //return m_data[i];
15 //直接复用上面的非常版本
16 returnconst_cast<Array&>(*this)[i];
17 }
18private:
19 int * m_data;
20 int m_size;
21};
22int main(void){
23 Array a(10);
24 a[0]=10;//a.operator[](0)->a.m_data[0]=10
25 a[1]=20;
26 cout<<a[0]<<','<<a[1]<<endl;//10,20
27 const Array& ra=a;
28 cout<<ra[0]<<','<<ra[1]<<endl;//10,20
29 //ra[0]=11; error const修饰不能修改值
30 //ra[1]=12; error
31 return 0;
32 }
6、解引用和间接成员访问操作符: * 、->
用于实现智能指针:一个封装常规指针的类类型对象,当它离开作用域时,其析构函数负责释放该常规指针所指向的动态内存,避免内存泄露。
代码:
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(void){
6 cout<<"A::A()"<<endl;
7 }
8 ~A(void){
9 cout<<"A::~A()"<<endl;
10 }
11};
12class PA{
13public:
14 PA(A* pa=NULL):m_pa(pa){}
15 ~PA(void){
16 if(m_pa){
17 delete m_pa; //释放栈区空间
18 m_pa=NULL;
19 }
20 }
21private:
22 A* m_pa;
23};
24 int main(void){
25 //A a;
26 //A* pa=new A;
27 PA pa(new A);//栈区对象
28 return 0;//智能指针:析构函数一定会被调用,防止内存泄露
29 }
tarena@tarena-virtual-machine:~/day42$./a.out
A::A()
A::~A()
实际使用:
#include<iostream>
#include<memory>
using namespace std;
class A{
public:
A(const string& str):m_str(str){
cout<<"A::A()"<<endl;
}
~A(void){
cout<<"A::~A()"<<endl;
}
string m_str;
};
class PA{
public:
PA(A* pa=NULL):m_pa(pa){}
~PA(void){
if(m_pa){
delete m_pa; //释放栈区空间
m_pa=NULL;
}
}
A* operator->(void)const{
return m_pa;
}
A& operator*(void)const{
return *m_pa;
}
private:
A* m_pa;
};
int main(void){
//A a;
//A* pa=new A;
#if 0
PA pa(new A("hello world"));//栈区对象
//pa.operator->()->m_str;
cout<<pa->m_str<<endl;
//pa.operator*().m_str;
cout<<(*pa).m_str<<endl;
#else
auto_ptr<A> pa(new A("hello world!"));//内部自动封装了只能指针
cout<<pa->m_str<<endl;
cout<<(*pa).m_str<<endl;
#endif
return 0;//智能指针:析构函数一定会被调用,防止内存泄露
}
7、类型转换操作符
1)通过构造函数实现自定义类型转换
基本类型-à类类型
Integer i (100);
//int -> Integer
i=200;//通过类型转换构造函数
2)通过类型转换操作符函数实现自定义类型转换
operator 目标类型(void)const { }
类类型à基本类型
//Integer àint
int i2=i ;//类型转换操作符函数
3)如果两个类型都是基本类型,无法转换
4)如果两个类型都是自定义类型,两种方式都可以
例:
1#include<iostream>
2using namespace std;
3class Integer{
4public:
5 //int ---> Integer
6 Integer(int data=0):m_data(data){
7 cout<<"类型转换构造函数"<<endl;
8 }
9 operator int(void)const{
10 cout<<"类型转换操作符函数"<<endl;
11 return m_data;
12 }
13private:
14 int m_data;
15};
16int main(){
17 Integer i1(100);//构造函数
18 i1=200;//类型转换构造函数,把i1转换成整型数
19 cout<<i1<<endl;//200 操作符函数
20 int i2=i1;//操作符函数
21 cout<<i2<<endl;//200
22 return 0;
23 }
类型转换构造函数
类型转换构造函数
类型转换操作符函数
200
类型转换操作符函数
200
8、new/delete 操作符
static void* operator new(size_t size){….}
static void operator delete(void *p){….}
举例:
1#include<iostream>
2#include<cstdlib>
3using namespace std;
4class A{
5public:
6 A(void){
7 cout<<"A::A()"<<endl;
8 }
9 ~A(void){
10 cout<<"A::~A()"<<endl;
11 }
12 static void* operator new(size_t size){
13 cout<<"A::new"<<endl;
14 void *pv=malloc(size);
15 return pv;
16 }
17 static void operator delete(void* pv){
18 cout<<"A::delete"<<endl;
19 free(pv);
20 }
21};
22int main(){
23 //1)分配内存
24 //A* pa=(A*)A::operator new(sizeof(A));
25 //2)pa->A();
26 A* pa=new A;
27
28 //1)pa->~A();
29 //2)释放内存
30 //A::operator delete(pa);
31 delete pa;
32 return0;
33 }
9、操作符重载限制
1)不是所有的操作符都能重载,下列操作符无法重载:
作用域限定操作符:“::”
直接成员访问操作符:“.”
直接成员指针解引用操作符:“.*”
条件操作符:“?”
字节长度操作符:“sizeof( )”
类型信息操作符:“typeid”
2)如果一个操作符所有的操作数都是基本类型,则无法重载
3)操作符重载不会改变编译器预定义的优先级
4)操作符重载无法改变操作数的个数
5)无法通过操作符重载发明新的操作符
6)操作符重载完全可以用普通成员函数来替换
一、继承(重点)!!!!(Inheritance)
1、继承的基本概念
通过一种机制表达类型之间共性和特性的方式,利用已有数据类型定义新的数据类型,这种机制就是继承。
人类:姓名、年龄、吃饭、睡觉
学生类:姓名、年龄、学号、吃饭、睡觉、学习
教师类:姓名、年龄、工资、吃饭、睡觉、讲课
人类:姓名、年龄、吃饭、睡觉
学生类继承人类:学号、学习
教师类继承人类:工资、讲课
人类(基类/父类)
/ \
学生类 教师类(派生类/子类)
基类派生子类,子类继承基类
2、继承的语法形式
class 子类:继承方式1 基类1,继承方式2,基类2……{
…..
}
继承方式:
public :公有继承
protected :保护继承
private:私有继承
class Human{};
class Student:public Human{
//Student类继承了Human类
//Student类存在一份Human中的成员
};
举例
#include<iostream>
using namespace std;
//人类(基类)
class Human{
public:
Human(const string& name,int age)
:m_name(name),m_age(age){}//初始化
void eat(const string& food)const{//常函数(const *this)不需要修改成员变量
cout<<"我在吃"<<food<<endl;
}
void sleep(int time)const{
cout<<"我睡了"<<time<<"小时"<<endl;
}
protected: //保护成员可以在子类中访问,外部不能访问
string m_name;
int m_age;
};
//学生类(人类派生的子类)
class Student:public Human{
public:
Student(const string& name,int age,int no):
Human(name,age),m_no(no){}//初始化
void who(void)const{//不修改成员变量的都尽量使用常函数
cout<<"我叫"<<m_name<<",今年"<< \
m_age<<"岁,学号"<<m_no<<endl;
}
void learn(const string& course)const{
cout<<"我在学"<<course<<endl;
}
private:
int m_no;
};
//教师类(人类派生的另外一个子类)
class Teacher:public Human{
public:
Teacher(conststring& name,int age,double salary):
Human(name,age),m_salary(salary){}
void teach(const string& course)const{
cout<<"我在讲"<<course<<endl;
}
void who(void)const{
cout<<"我叫"<<m_name<<",今年"<< \
m_age<<"岁,工资:"<<m_salary<<endl;
}
private:
double m_salary;
};
int main(void){
Student s("悟空",30,10001);
s.who();
s.eat("桃子");
s.sleep(8);
s.learn("打坐");
Teacher t("唐僧",31,5000.5);
t.who();
t.eat("蔬菜");
t.sleep(6);
t.teach("佛法");
return 0;
}
tarena@tarena-virtual-machine:~/day42$./a.out
我叫悟空,今年30岁,学号10001
我在吃桃子
我睡了8小时
我在学打坐
我叫唐僧,今年31岁,工资:5000.5
我在吃蔬菜
我睡了6小时
我在讲佛法
3、公有继承的特性
3、1共同性:任何时候子类对象都可以看做是一个基类对象
3、2向上造型(重点)
将子类类型的指针或引用转换成基类类型的指针或引用。
(将学生类型转换成老师类型)
这种转换缩小了指针或引用操作范围,在编译看来是安全的,所以可以隐式转换。
#include<iostream>
using namespace std;
//人类(基类)
class Human{
public:
Human(const string& name,int age)
:m_name(name),m_age(age){}//初始化
void eat(const string& food)const{//常函数(const *this)不需要修改成员变量
cout<<"我在吃"<<food<<endl;
}
void sleep(int time)const{
cout<<"我睡了"<<time<<"小时"<<endl;
}
protected: //保护成员可以在子类中访问,外部不能访问
string m_name;
int m_age;
};
//基类子对象:子类对象中包含基类的部分
//学生类(人类派生的子类)
class Student:public Human{
public:
Student(const string& name,int age,int no):
Human(name,age),m_no(no){}//初始化
void who(void)const{//不修改成员变量的都尽量使用常函数
cout<<"我叫"<<m_name<<",今年"<< \
m_age<<"岁,学号"<<m_no<<endl;
}
void learn(const string& course)const{
cout<<"我在学"<<course<<endl;
}
private:
int m_no;
};
//教师类(人类派生的另外一个子类)
class Teacher:public Human{
public:
Teacher(const string& name,int age,double salary):
Human(name,age),m_salary(salary){}
void teach(const string& course)const{
cout<<"我在讲"<<course<<endl;
}
void who(void)const{
cout<<"我叫"<<m_name<<",今年"<< \
m_age<<"岁,工资:"<<m_salary<<endl;
}
private:
double m_salary;
};
int main(void){
Student s("悟空",30,10001);
s.who();
s.eat("桃子");
s.sleep(8);
s.learn("打坐");
Teacher t("唐僧",31,5000.5);
t.who();
t.eat("蔬菜");
t.sleep(6);
t.teach("佛法");
//向上造型,缩小了指针的操作范围,所以安全,可以做隐式类型转换
//(12个字节只访问前8个字节,安全!)
Human* ph=&s; //ph指向子类对象的基类指针
ph->eat("桃");//可以访问基类中有的部分
ph->sleep(10);//可以访问基类中有的部分
//ph->who();
return 0;
}
图:向上造型
练习:M33矩阵类让它增加支持如下操作符:
前后++ 前后-- 取负(让无参构造对象减去有参构造对象)、取下标[ ]值
arr[1][1]=>*(arr+1)[1]=>*(*(arr+1)+1)
#include <iostream>
#include <iomanip>//setw()设置域宽
using namespace std;
class M33{
public:
M33(void){
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
m_a[i][j]= 0;
}
M33(inta[][3]){
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
m_a[i][j]= a[i][j];
}
friendostream& operator<<(ostream& os,
constM33& m){
for(inti=0; i<3; i++){
for(intj=0; j<3; j++)
os<< setw(4) << m.m_a[i][j];
os<< endl;
}
returnos;
}
//+- *:返回值右值,左右操作数可以右值也可
//以为左值
constM33 operator+(const M33& m)const{
inta[3][3]={};
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
a[i][j]=m_a[i][j]+m.m_a[i][j];
returna;
}
constM33 operator-(const M33& m)const{
inta[3][3]={};
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
a[i][j]=m_a[i][j]-m.m_a[i][j];
returna;
}
constM33 operator*(const M33& m)const{
inta[3][3]={};
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
for(intk=0; k<3; k++)
a[i][j]+=
m_a[i][k]*m.m_a[k][j];
returna;
}
//+=-= *=:返回值左值,左操作数必须是左值,右
//操作数可以为左值也可以为右值
M33&operator+=(const M33& m){
return*this = *this + m;
}
M33&operator-=(const M33& m){
return*this = *this - m;
}
M33&operator*=(const M33& m){
return*this = *this * m;
}
constM33 operator-(void)const{
returnM33() - *this;
}
//前++ --,操作数左值,返回值左值
M33&operator++(void){
for(inti=0; i<3; i++)
for(intj=0; j<3; j++)
++m_a[i][j];
return*this;
}
M33&operator--(void){
for(inti=0; i<3; ++i)
for(intj=0; j<3; j++)
--m_a[i][j];
return*this;
}
//后++ --,操作符左值,返回值右值
constM33 operator++(int){
M33m = *this;
++*this;
returnm;
}
constM33 operator--(int){
M33m = *this;
--*this;
returnm;
}
int*operator[](int i){
returnm_a[i];
}
constint* operator[](int i)const{
//复用上面的版本
returnconst_cast<M33&>(*this) [i];
}
private:
intm_a[3][3];
};
int main(void)
{
inta1[3][3]={1,2,3,4,5,6,7,8,9};
inta2[3][3]={9,8,7,6,5,4,3,2,1};
M33m1(a1);
M33m2(a2);
cout<< "-m2:" << endl;
cout<< -m2 << endl;
cout<< "++m2:" << endl;
cout<< ++m2 << endl;
cout<< m2 << endl;
cout<< "--m2:" << endl;
cout<< --m2 << endl;
cout<< m2 << endl;
cout<< "m2++:" << endl;
cout<< m2++ << endl;
cout<< m2 << endl;
cout<< "m2--:" << endl;
cout<< m2-- << endl;
cout<< m2 << endl;
cout<< "下标:"<< endl;
for(inti=0;i<3;i++){
for(intj=0;j<3;j++)
m2[i][j]+= 10;
}
cout<< m2 << endl;
constM33& cm = m2;
//cm[1][1]= 20;//error
return0;
}