类
class test{
private:
默认为private
public:
};
#函数
构造函数
-
系统自动调用的初始化函数
-
函数名与类名相同
-
无返回值
-
公有函数(必须)
-
可以重载
-
可以带默认形参
(一般用于初始化值)
- 成员初始化列表
在无const和引用类型时,通常使用(this指针)初始化,若存在const和引用类型时,用初始化列表初始化(如下)。
- 普通成员(无const、static)初始化时可以直接赋值
-
- 类内常成员必须使用初始化列表进行初始化
class Student{
private:
const int ID;
string name;
int age;
const bool gender;
float *score;
int scoreLength;
public:
//无参数构造函数
Student():ID(2021013274),gender(true)
{
this->name="李尧";
this->age=19;
this->score=new float [2]{(100),(100)} ;
this->scoreLength=2;
}
int main()
{
//无参数
Student a;
return 0;
}
//有参数构造函数
Student (const int ID,string name, int age,const bool gender,float *score,int scoreLength):ID(ID),gender(gender)
{
this->name=name;
this->age=age;
this->scoreLength=scoreLength;
this->score= new float [scoreLength];
for(int i=0;i<scoreLength;i++)
{
this->score[i]=score[i];
}
}
};
-
若在带参数后面直接声明参数值,则可少些一种不参的构造函数
Point2D(float x=0,float y=0) { this->x=x; this->y=y; } //相当于写了两种构造函数,一种含参,另一种不含参。
###析构函数
-
对象消失时的清理工作
-
释放单元等
-
析构函数不能被重载
-
析构函数不可以带参数
~WowID(){ this->age=0; this->name=""; this->scoreLength=0; delete []this->score; }
数组用delet、 数值归零、string类型变为“”(空)、
拷贝构造函数
- 浅拷贝:
WowID herID(myID)
把myID的值给herID,
- 上述系统自动生成的拷贝构造函数
- 缺点:难以应对指针和引用参
- 深拷贝:需要重载构造函数
//深拷贝构造函数
Student(const Student &copied):ID(copied.ID),gender(copied.gender)
{
this->name=copied.name;
this->age=copied.age;
this->scoreLength=copied.scoreLength;
this->score= new float[scoreLength];
for(int i=0;i<scoreLength;i++)
{
this->score[i]=copied.score[i];
}
}
- 参数为引用类型
成员函数
//求平均score的函数
float py()
{
float a=0;
for(int i=0;i<this->scoreLength;i++)
{
a=a+this->score[i];
}
return a/this->scoreLength;
}
复杂对象
对象指针
-
指向对象存放的地址
-
类名==*==对象指针名;对象指针名->成员变量名;
对象指针名->成员函数名;
动态对象
- 类名 对象指针名=new 类名(构造参数);
-
只建立一个对象
-
int *a=new int(120);
申请一个整型变量空间,赋初值为120,并定义一个整型指针a指向该地址空间
- delete 对象指针名;
- 类名 对象指针名=new 类名[n];
- 建立一组类
- delete []对象指针名;
- 结合1,2
-
Student *stu new Student[2]{{"1"},{"2"}}; 既建立多个对象,又分别初始化
对象数组
-
以对象为元素的数组
-
类名 对象数组名[n];
-
数组名[n]={类名(构造参数表1), 类名(构造参数表2), … 类名(构造参数表n)};一定要加类名
-
WowID a[5] = {WowID(106067, "部落猪联盟狗", 1), WowID(106068, "呀哈哈", 1), WowID(106069, "杨凌彭于晏", 0), WowID(106070, "黄忠", 1), WowID(106071, "战士冲锋", 0)};
-
组合对象
-
类中含有其他类的对象作为成员
-
初始化:
组合类名(参数表):成员对象1(子参数表1), 成员对象2(子参数表2)
- 构造函数调用顺序 ,按照组合类的声明中出现的次序 ,与初始化列表无关
class KingAcc{
private:
int ID; string name;
public:
KingAcc;
KingAcc(int id, string name);
};
class KingTeam{ //组合类名
private:
string name;
KingAcc cap; KingAcc mem;
public:
KingTeam(string name, KingAcc cap,
KingAcc m):mem(m), cap(int id,string name){
this->name = name;
}
};
//例二
class Circle{
private:
Point2D center;
float radius;
static int Num;
public:
const float PI;
Circle(float x=0,float y=0, float r=0):center(x,y),PI(3.14f)
{
this->r=radius;
}
static和const成员
静态成员
- 不同对象数据成员和函数的共享,在内存中只对应一个存储区域。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zo2FGUVg-1651677360648)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220331202543174.png)]
- 在程序运行开始之前就存在
- 静态成员函数无this指针
- 静态成员函数
static 数据类型 静态成员名;
static 返回类型 函数名;
- 静态成员初始化方法(一般在主函数之前)
数据类型(const) 类名::静态成员名;
int Point2D::number = 0;
static const int e; //类内声明
const int ReviewConst::e = 5; //类外
- 不用再写static
const成员函数
-
返回类型 成员函数名(参数表) const;
- 常用于访问器函数,
- 修改器函数(不能使用const)
class KingAcc{ private: int ID; string name; public: int getID() const{ return this->ID; } string getName() const{ return this->name; } }; //类外定义时 string KingAcc::getName() const { return this->name; }
- 注意事项
- 不能更新数据成员
- 不能调用非常成员函数
- 定义和声明时都要加const
友元
##友元函数
- 在类中声明,在类外定义
- 可以调用和修改私有成员
friend float Distance(Point2D a,
Point2D b);
};//类中
float Distance(Point2D a, Point2D b)
{
//类外定义
}
友元类
• 单向性
• A类被声明为B类的友元
• A类成为B类的友元类
• A类所有成员函数为B的友元函数
>>A可以调用B
• 但B类不能调用A类的私有成员
class B{
...
friend class A;
};
继承与派生
定义及语法
- 原有类:基类(父类)
- 新类:派生类(子类)
class 派生类名:继承方式 基类1,继承方式 基类2{
private:
派生类私有成员
public:
派生类公有成员
protected:
派生类保护成员
};
派生类构造函数
派生类名(参数表):基类名1(参数表1),基类名n(参数表n)
{
派生类新增成员初始化
}
多层继承时在参数列表应该包含最初的基类
-
继承方式:
public:公有继承
private:私有继承
proteted:保护继承(未发生继承时和私有类型一样)
继承方式\基类属性 | public | protected | private |
---|---|---|---|
public | public | protected | 不可访问 |
protected | protected | protected | 不可访问 |
private | private | private | 不可访问 |
类型兼容
定义:
-
在公有派生的情况下,一个派生类对象可作为基类的对象来使用
-
派生类的对象可以赋值给基类对象
-
派生类对象可以初始化基类的引用
-
派生类对象的地址可赋给指向基类的指针
###成员变量、函数覆盖
class A{
public:
void Func()
{
cout<<"A"<<endl;
}
};
class B:public A{
public:
void Func()
{
cout<<"B"<<endl;
}
};
int main(){
B b;
b.Func();
return 0;
}
输出为B
int main()
{
B b;
A a1(b);
A *a2 = &b;
A &a3(b);
a1.Func();
a2->Func();
a3.Func();
return 0;
}
输出均为A
说明:用子类(B)对象初始化父类(A),但仍是声明时的父类(A),只是用子类赋值父类而已
-
**子类对象可以初始化父类对象 **
-
父类对象不能初始化子类对象
##多继承
- 派生类继承多个基类,调用时,按照继承时声明顺序从左向右
二义性问题
- 派生类的多个基类中拥有同名成员,调用该成员将出现二义性
分类:
- 间接二义性
- 直接二义性
解决方法
虚基类:
###定义
- class 派生类名:virtual 继承方式 基类名,virtual 继承方式 基类名
作用
- 虚基类构造函数只被调用一次
重载
函数重载
- 相同功能不同数据类型(或参数个数)
- 使用同名函数实现
区分依据:
- 函数名
- 参数类型(非指针和引用上的const无法区分)
- 参数个数
- 返回类型无法区分
double getEarning(double salary, int absenceDays)
{
double a;
return a=salary-absenceDays*salary/22;
}
double getEarning(double baseSalary, double salesSum, double rate)
{
double a;
return a=baseSalary+salesSum*rate;
}
double getEarning(int workPieces, double wagePerPiece)
{
double a;
return a=workPieces*wagePerPiece;
}
运算符重载
- 说明:
- 同一个运算符作用于不同类型数据时
- 实现功能相近的操作
可重载运算符
不可重载运算符
-
重载规则
- 重载后运算符的优先级和结合性不变
- 运算符操作数的个数不能改变
- 不能重载C++中不支持的运算符 如@ # $等
- 保持运算符的语义
-
运算符重载方式
- 定义:返回类型 operator运算符(形参表)
(1)重载为类的成员函数
- 可以省略一个形参,另一个参数实际通过this指针隐式传递
eg:
Complex operator=(const Complex& a){
Complex b;
b.real = a.real;
b.imag = a.imag;
b.print();
b.print();
return b;
}
Complex a(1,1);
Complex c(0,0);
c = a;
c.print();
输出1+1i,0+0i
原因:c=a,调用的是c的成员函数,然后返回出中间参数b,但是没有变量收到中间参量的值,所以c是啥还是啥,相当于
把c=a看成一个整体,运行完c=a,后直接返回中间参量b
若改成c=(e=a)就对了
或者直接变成变量本身
Complex operator=(const Complex& a){
this->real = a.real;
this->imag = a.imag;
return *this;(即类本身)
}
Complex a(1,1);
Complex c(0,0);
c = a;
c.print();
(2)重载为类的友元函数
-
语法:friend 返回类型 operator 运算符(形参表)[需要给出所有参数];
-
友元函数没有this指针
-
若使用==单目运算符且修改数据成员需要使用引用类型==
eg:
friend Complex operator--(Complex &a);
friend Complex operator--(Complex &a, int x);
Complex operator*(int x){ Complex result; result.real = x*this->real; result.imag = x*this->imag; return result; } friend Complex operator*(int x, Complex &a); Complex operator*(int x, Complex &a){ Complex r(a); r.real *= x; r.imag *= x; return r; } int main() { Complex a(1,1); a=a*6; a=6*a; return 0; }
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLHehxE5-1651677360650)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220503153426928.png)]
- 典型运算符重载(<< >>)
friend istream &operator>>(istream &in,Complex &c);
istream &operator>>(istream &in, complex &C)
{
in>>c.real>>c.imag;
return in;
}
friend ostream &operator<<(ostream &out,Complex c);
ostream &operator<<(ostream &out,Complex c)
{
out<<c.real<<"+"<<c.imag<<"i"<<endl;
return out;
}
int main()
{
Complex a,b;
cin>>a>>b;
cout<<a<<b;
return 0;
}
运算符函数与返回类型
-
如果仅为只读参数的值,应作为==const &==来传递
Complex operator+(const Complex& a) { Complex result; result.real = a.real + this->real; result.imag = a.imag + this->imag; return result; }
-
如果产生一个新值,就需要产生一个作为返回值的新对象,这个对象作为一个常量通过传值方式返回
const Complex operator+(const Complex& a)
{
Complex result;
result.real = a.real + this->real;
result.imag = a.imag + this->imag;
return result;
}
- 如果函数返回的是原有对象,通常以引用方式返回,根据是否希望对返回的值进行运算来决定是否返回const &
Complex &operator()(int r, int i)
{
this->real = r;
this->imag = i;
return *this;
}
const Complex &operator()(int r, int i)
{
this->real = r;
this->imag = i;
return *this;
}
lex& a)
{
Complex result;
result.real = a.real + this->real;
result.imag = a.imag + this->imag;
return result;
}
2. 如果==产生一个新值==,就需要产生一个作为返回值的新对象,这个对象作为一个==常量==通过传值方式返回
```c++
const Complex operator+(const Complex& a)
{
Complex result;
result.real = a.real + this->real;
result.imag = a.imag + this->imag;
return result;
}
- 如果函数返回的是原有对象,通常以引用方式返回,根据是否希望对返回的值进行运算来决定是否返回const &
Complex &operator()(int r, int i)
{
this->real = r;
this->imag = i;
return *this;
}
const Complex &operator()(int r, int i)
{
this->real = r;
this->imag = i;
return *this;
}