大一下第二学期期中知识复习梳理 之 c++类与对象
一、面向对象的程序设计
1、封装性(一定有)

相同/不同类型封装成一个整体
(二)操作
1、结构类型
struct 结构类型名
{
类型名 变量1;
类型名 变量2;
};
例子:定义复数结构类型
2、结构变量
1)定义:关键字:struct
方式1:struct 结构类型名 结构变量名1,结构变量名2;
方式2:结构类型名 结构变量名1,结构变量名2;
方式3:定义结构类型同时定义结构变量。
2)初始化
结构类型名 结构变量名={初始化列表};
系统往往不会对局部变量整形数和浮点型数初始化为0.
若个数相同,则一一对应;若初值个数<变量个数,由前往后赋初值,无初值的后面系统自动设置。
3)操作
3、结构指针
例子:
三、类
(一)概念
(二)成员
(三)定义
1、类的定义
class 类名{访问限定符:成员列表};
private:写后除非通新的public,其它之后皆private,也可省略。第一个private可省。
一般数据成员定义为private类型,函数成员定义为public类型。
2、函数成员的定义
方式一:类外定义,类中声明:
返回值类型 类名:: 函数名(参数表){......};
方式二:类内定义+声明:(同一般函数定义)
返回值类型 函数名(参数表){......};
(四)对象
1、概念
2、使用(同变量)
1)对象的创建
定义格式:类名 对象名
例子:complex x;
2)对象数组的创建
定义格式:类名 对象名[数组个数];
例子:complex x[10];
3)对象指针的创建
a) 定义格式:类名 *对象指针名;
例子:complex *py;
b) 初始化:指向已定义的其他同类对象(使用前须初始化!)
例子:complex x,*py; py=&x;
c) 存储空间:注意创建对象指针时,并未对该指针指向的地址分配存储空间
3、对象成员的操作
通过类对象访问,只能访问公有成员。
只能在类内部(成员函数中) 访问私有成员,不能在类外部(通过类对象访问私有成员)。
类与对象应用实例1:

#include<iostream> #include<iomanip> #include<cstring> using namespace std; class CGoods { char Name[21]; int Amount; float Price; float Total_value; public: void RegisterGoods(char [],int,float); void CountTotal(void) ; void GetName(char[]) ; int GetAmount(void) ; float GetPrice(void) ; float GetTotal_value(void) ; }; void CGoods::RegisterGoods(char name[],int amount,float price) { strcpy(Name,name); Amount=amount; Price=price; } void CGoods::CountTotal(void) { Total_value=Price*Amount; } void CGoods::GetName(char name[]) { strcpy(name,Name); } int CGoods::GetAmount(void) { return Amount; } float CGoods::GetPrice(void) { return Price; } float CGoods::GetTotal_value(void) { return Total_value; } int main() { CGoods car; char str[21]; int number; float pr; cout<<"请输入汽车型号:" ; cin.getline(str , 20) ; cout<<"请依次输入汽车数量与单价:" ; cin>>number>>pr ; car.RegisterGoods(str,number,pr); car.CountTotal(); str[0]='\0'; car.GetName(str); cout<<setw(20)<<str<<setw(5)<<car.GetAmount() ; cout<<setw(10)<<car.GetPrice()<<setw(20)<<car.GetTotal_value()<<endl ; return 0; }
3、特殊的成员函数

方式一:类内定义:
类名 (参数列表) {...};
方式二:类外定义,类内声明:
类内声明:类名(参数列表);
类外定义:类名:: 类名 (参数列表){...};
特:构造函数可带默认参数,设定默认值,在声明中指定:可应对任意个数参数指定的所有情况全覆盖。
例如:类内声明时:类名(参数类型1 参数名1=默认值1,参数类型2 参数名2=默认值2,...);
c)使用
对象创建时自动调用
构造函数应用实例:

#include<iostream> #include<iomanip> #include<cstring> using namespace std; class CGoods { char Name[21]; int Amount; float Price; float Total_value; public: CGoods(); CGoods(char [],int, float); CGoods(char[],float); void GetName(char[]) ; int GetAmount(void) ; float GetPrice(void) ; float GetTotal_value(void) ; }; CGoods::CGoods() { Name[0]='\0'; Price=0.0; Amount=0; Total_value=0.0; } CGoods::CGoods(char name[],float price) { strcpy(Name,name); Price=price; Amount=0; Total_value=0.0; } CGoods::CGoods(char name[],int amount,float price) { strcpy(Name,name); Price=price; Amount=amount; Total_value=Price*Amount; } void CGoods::GetName(char name[]) { strcpy(name,Name); } int CGoods::GetAmount(void) { return Amount; } float CGoods::GetPrice(void) { return Price; } float CGoods::GetTotal_value(void) { return Total_value; } int main() { char str[21]; int number; float pr; cout<<"请输入汽车型号:" ; cin.getline(str , 20) ; cout<<"请依次输入汽车数量与单价:" ; cin>>number>>pr ; CGoods car(str,number,pr); str[0]='\0'; car.GetName(str); cout<<setw(20)<<str<<setw(5)<<car.GetAmount() ; cout<<setw(10)<<car.GetPrice()<<setw(20)<<car.GetTotal_value()<<endl ; return 0; }
2)通过已有的对象初始化对象:复制构造函数
a)概念
b)声明和定义
类名(类名 & 类对象){};
c)使用
复制构造函数被自动调用的情况:
途径一:一个对象通过另一个对象初始化时
注:complex c1,c2; c1=c2;(×)此时不为初始化,需调用运算符重载函数。
途径二:一个对象以值传递的方式传入函数时
途径三:一个对象以值传递的方式从函数返回时
注意:值传递时,复制给一个无名的临时对象。
返回局部变量时不可引用:局部变量出了函数后离开了其局部生命域;引用传递是通过获取地址访问变量,离开后局部变量被释放,原地址作废,出错。
complex add(complex c1①,complex c2②) { complex c3③; c3.real=c1.real; c3.image=c2.image; return c3④; }
解析:调用3次复制构造函数①②③;调用1次构造函数④;调用4次析构函数(调用多少次构造(复制构造)函数即在离开此函数结尾调用多少次析构函数)。
此处函数若定义为complex & add():不可。c3为局部变量。同理,此函数中所有complex换为int类型定义,也不可int &add()。

4、成员对象
(1)概念

(2)初始化

(3)构造函数执行过程
(4)析构函数


#include<iostream> #include<cstring> using namespace std; class studentID { long value; public: studentID(long id=0) { value=id; cout<<"赋给学生的学号:"<<value<<endl; } ~studentID(){cout<<"删除学号:"<<value<<endl;} }; class student { studentID id; studentID pin; char name[20]; public: student(char sname[]="no name",long sid=0):pin(222),id(sid) { cout<<"学生名:"<<sname<<endl; strcpy(name,sname); } ~student(){cout<<"删除学生名:"<<name<<endl;} }; int main(){ student ss("朱明",82020132); return 0; }
5、this指针
(1)概念
只读的指针变量,只有找本身不能改对象。
(2)用法
常见用法:
1、return *this;中,返回为类对象而非类对象指针。
2、类名 类对象名(*this);调用复制构造函数先存下当前的“我”。
总结:可引用传递的情况:传全局变量地址;非局部变量,其他变量自定义的空间、生命域不仅限于当前函数。
四、运算符重载
(一)针对对象(适用范围)

定义分类:
1、单目运算符:右无参数,左操作数调用。
实例:
class zoo{ int n_tiger, n_lion; public: zoo operator++() //前置,++a ++n_tiger; ++n_lion; return *this; } zoo operator++(int) //后置,a++ zoo tmp(*this); ++n_tiger; ++n_lion; return tmp; } }
2、双目运算符:1个参数。左操作数调用函数,右操作数为参数。
实例:
class complex{ private: double real, image; public: complex(double r=0.0, double i=0.0) {real=r; image=i;}; complex(complex &c) {real=c.real; image=c.image;}; void Print(){ cout<<"Real="<<Real<<'\t'<<"Image="<<Image<<'\n'; } complex operator+(complex); //复数相加 complex operator+(double); //复数+实数 complex operator=(complex); //复数赋值 complex operator+=(complex); double abs(void); complex operator*(complex); //复数相乘 complex operator/(complex); //复数相除 };
(三)应用实例:复数运算
不能改为引用原因:局部参数返回值地址无用。
int main(void){ complex c1(1.0,1.0) , c2(2.0,2.0) , c3(4.0,4.0) , c; double d=0.5 ; c1.Print(); c=c2+c3; //操作1:对象c2执行c2+c3的操作;操作2:对象c执行c=(操作1返回值)的操作 c.Print(); c+=c1; c.Print(); c=c+d; c.Print(); //复数加实数 c=c3*c2; c.Print(); c=c3/c1; c.Print(); cout<<"c3的模为:"<<c3.abs()<<endl; c=c3=c2=c1; //操作1:对象c2执行c2=c1的操作;操作2:对象c3执行c3=(操作1返回值)的操作;操作3:对象c执行c=(操作2返回值)的操作 c.Print(); //连续赋值 c+=c3+=c2+=c1; //操作1:对象c2执行c2+=c1的操作;操作2:对象c3执行c3+=(操作1返回值)的操作;操作3:对象c执行c+=(操作2返回值)的操作 c.Print(); //连续加赋值 return 0; }
运算符重载函数实例:

#include<iostream> #include<cmath> using namespace std; class complex { double real,image; public: complex(double r=0.0,double i=0.0) {real=r;image=i;} complex(complex &c) {real=c.real;image=c.image;} ~complex(){static int tot=0;cout<<++tot<<":"<<"Real="<<real<<'\t'<<"Image="<<image<<'\n';} void Print(){cout<<"Real="<<real<<'\t'<<"Image="<<image<<'\n';} complex operator +(complex); complex operator +(double); complex operator =(complex); complex operator +=(complex); double abs(); complex operator *(complex); complex operator /(complex); }; /*complex::complex(double r,double i) { real=r; image=i; } complex::complex(complex &c) { real=c.real;image=c.image; }*/ complex complex::operator +(complex a) { /*complex tmp; tmp.real=real+a.real; tmp.image=image+a.image;*/ return complex(real+a.real,image+a.image); } complex complex::operator +(double d) { /*complex tmp; tmp.real=real+a;*/ return complex(real+d,image); } complex complex::operator =(complex a) { real=a.real; image=a.image; return *this; } complex complex::operator +=(complex a) { real+=a.real; image+=a.image; return *this; } double complex::abs() { return sqrt(real*real+image*image); } complex complex::operator *(complex a) { double tr,ti; tr=real*a.real-image*a.image; ti=image*a.real+a.image*real; return complex(tr,ti); } complex complex::operator /(complex a) { double tr,ti; tr=(real*a.real+image*a.image)/(a.real*a.real+a.image*a.image); ti=(image*a.real-a.image*real)/(a.real*a.real+a.image*a.image); return complex(tr,ti); } int main() { complex c1(1.0,1.0) , c2(2.0,2.0) , c3(4.0,4.0) , c; double d=0.5 ; c1.Print(); c=c2+c3; c.Print(); c+=c1; c.Print(); c=c+d; c.Print(); //复数加实数 c=c3*c2; c.Print(); c=c3/c1; c.Print(); cout<<"c3的模为:"<<c3.abs()<<endl; c=c3=c2=c1; c.Print(); //连续赋值 c+=c3+=c2+=c1; c.Print(); //连续加赋值 return 0; }
五、友元函数
(一)声明定义
多与操作符重载函数关联使用
声明:friend 类名 operator 操作符(参数列表);
eg:(参数类型1,const 参数类型2 &) 对应(左操作数,右操作数)
定义:类名 operator 操作符(参数列表){};
定义时不要加friend,也不是成员函数。
(二)使用:同内置数据类型直接使用
例子:
class complex{ … //声明 friend complex operator+(double, const complex&); }; //定义时不需再加friend,也不是成员函数 complex operator+(double d, const complex& c) { return complex(d+c.real, c.image); } int main(){ … //使用 c=d+c1; //相当于c=operator+(d, c1) }
(三)与操作符重载函数差别
1.可用友元函数实现但无法用操作符重载函数实现的情况:
双目运算符,第一个操作数为内置数据类型。eg:实数+复数
只能用友元函数实现:<< >>
2.可用操作符重载函数实现但无法用友元函数实现的情况:
= [] () →
(四)使用注意
右值可为常量,左值必须为变量(可修改),但在友元函数中左右两值地位平等,故需要避免将左操作数作常量。
(五)友元类
具有单向性:person可动用pet的所有的函数等,修改私有成员;但pet不可对person动用。
六 、静态成员
(一)概念和内容
#include <iostream> using namespace std; class Ctest{ private: static int count; public: Ctest() {++count; cout<<"对象数量="<<count<<'\n'; } ~Ctest() {--count; cout<<"对象数量="<<count<<'\n'; } }; //对静态数据定义性说明 int Ctest::count=0; int main(void){ Ctest a[3]; return 0; } 编译结果: 对象数量=1 //a[0]构造函数产生 对象数量=2 //a[1]构造函数产生 对象数量=3 //a[2]构造函数产生 对象数量=2 //a[2]析构函数产生 对象数量=1 //a[1]析构函数产生 对象数量=0 //a[0]析构函数产生
七、总结
(一)四个带系统默认的函数:
构造函数,复制构造函数,析构函数,赋值操作符重载函数(例如 complex x=y;)。
(二)封装的两种方式(非内置数据类型)
1、结构:
1)封装数据;
2)访问限定:公有。访问到结构对象即可访问其成员。
2、类与对象:
1)封装数据与函数;
2)访问到类对象不一定可以访问其成员:public型都可通过类对象访问,private只有在类内部成员函数中访问,其他参数/局部变量类对象也都可访问。