July.2.2021:
一、第一个类程序。
首次接触面向对象的类(class),感觉有点像C语言的结构体(struct),但是不同。有如下区别:
- 类具有权限管理,默认为私有变量。而结构体是公有变量。
- 类可以有函数成员,而结构体不能。而且函数成员可以是重载函数和带默参数值的函数。
#include <iostream>
using namespace std;
// 时钟类定义
class Clock{
public:
void setTime(int newH,int newM,int newS); // 设置时间
inline void showTime(); // 显示时间
private:
int hour,minute,second;
};
// 主函数
int main(int argc, const char * argv[]) {
Clock myClock; // 默认初始化,数据成员被初始化为0
cout<<"第一次默认输出:";
myClock.showTime();
myClock.setTime(8, 29, 59);
cout<<"输入修改后输出:";
myClock.showTime();
return 0;
}
// 函数定义
void Clock::setTime(int newH,int newM,int newS){
hour = newH;
minute = newM;
second = newS;
}
inline void Clock::showTime(){
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
上面程序,是根据网课视频老师的程序模仿出来的。不是很清楚的地方是:“类的初始化”。另外,一定要记得在类定义中,函数成员声明要写上返回值类型。
二、类的定义及访问
1、基本定义形式
class 类名{
public: // 公有成员
void func1(); ……;
private: // 私有成员
int vcalue1,value2,……;
protected: //保护型成员
……;
}
// 类函数成员定义
// 形式1:类外定义
类名::func1(){
……; // 具体实现
}
// 形式2: 直接在类内定义,形成内联(inline)成员函数(函数体内不能有“循环”、“switch”等语句)。
2、访问
如果在类作用域内,可以直接用下面方式访问;如果在做用域外,需在前面加上“类作用域限定符”,如:类::func1();
类名.func1();
类名.value1;
July.3.2021:
三、类的初始化
1、类内初始化
类内初始化,实质就是类内数据成员的初始化,即为数据成员指定初始值。具体如下:
// 类内初始化
class Clock{
public:
void setTime(int newH,int newM,int newS);
inline void showTime();
private:
int hour=0,minute=0,second=0; // 类内初始化,0称为类内初始值
};
⚠️注意:构造类对象时,如果类包含构造函数,则优先根据构造函数指定值初始化,如果没有构造函数,则根据类内初始值初始化。
2、构造函数:初始化算法
构造函数,是在构造对象时,将对象初始化到特定的状态。例如:
// 构造函数
class Clock{
public:
Clock(int newH,int newM,int newS); // 带参数的构造函数
Clock()=default; // 默认构造函数,C++11
void setTime(int newH,int newM,int newS);
inline void showTime();
private:
int hour,minute,second; // 类内初始化,0称为类内初始值
};
// 构造函数定义
Clock::Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS){}; // 列表初始化
// 调用构造函数(自动调用)
Clock myClock(0,0,0); // 调用带参数的构造函数
Clock myClock; // 调用无参数构造函数
(1)定义构造函数
- 语法规则为:
- 函数名与类名相同;
- 不能定义返回值类型,也不能有return语句;
- 可以有形参,也可以没有;
- 可以内联、重载、带默认值。
- 调用方式:不用显式调用,而是对象被创建时自动调用。
⚠️以下不是正确的函数重载形式,不能一同出现在类中
//默认构造函数:调用时不需要实参的构造函数
Clock(int newH=0,int newM=0,int newS=0); // #1 带默认值的构造函数
Clock(); // #2 无参数的构造函数
(2)当未在类中定义构造函数时,系统会自动生成默认构造函数。但是,默认构造函数不会为数据成员设置初始值。所以,当数据成员为类内初始化时,使用类内定义值;如果没有类内初始化,则其值是不确定的。
特别地,类内定义构造函数后,系统不会再自动生成默认构造函数,如果仍然需要默认构造函数,则使用下面语法
Clock()=default; // 默认构造函数,C++11
(3)委托构造函数
委托构造函数,是为了减少编程时工作量,增强代码重用而来。例如:
// 声明构造函数
Clock(); // #1 默认构造函数
Clock(int newH,int newM,int newS); // #2 带参数的构造函数
// 定义构造函数
Clock::Clock():hour(0),minute(0),second(0){}; // #1
Clock::Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS){};// #2
// 委托构造函数:#1委托#2构造对象
Clock:Clock(0,0,0){};
(4)拷贝(复制)构造函数
拷贝构造函数,其原理和C语言中,赋值、函数间传递参数时(非指针),传递的实质是参数副本的原理一致。C++只是为其生成了一个类的临时对象供传递,复制构造函数的工作是将当前对象的数据拷贝至临时对象(一一对应)。即用一个已存在的对象去初始化同类型的新对象。
- 当类中没有定义复制构造函数时,系统会生成一个默认的拷贝构造函数。
- 语法遵循构造函数语法,但是参数列表为
(const 类名 &对象名)
。
- 要求系统不自动生成默认复制构造函数,有如下做法:
C++98:将复制构造函数声明为private,不提供函数实现;
C++11:在类声明中,在复制构造函数后追加=delete
即可。
(5)析构函数
析构函数的工作是,当对象消亡时,完成相关的善后工作。
- 当没有定义析构函数时,编译器会自动生成默认的析构函数(函数体为空)。
- 无参数,无返回值。
- 语法:
~类名();
构造函数举例
//构造函数例子
#include <iostream>
using namespace std;
unsigned int i=0,j=0; // 跟踪进入构造、复制构造函数的次数
class Point{
public:
Point(int xx,int yy){
x=xx;
y=yy;
++i;
cout<<"第"<<i<<"次进入构造函数"<<endl;
} // 内联构造函数
Point(const Point &p); // 复制构造函数
int getX() const{return x;}
int getY() const{return y;}
inline void setX(int xx){x=xx;}
inline void setY(int yy){y=yy;}
private:
int x,y;
};
Point::Point(const Point &p){
x=p.x;
y=p.y;
++j;
cout<<"第"<<j<<"次进入拷贝构造函数"<<endl;
}
void func1(Point p);
Point func2();
int main(int argc, const char * argv[]) {
Point a(4,5); // 构造
Point b=a; // 拷贝构造
func1(b); // 拷贝构造,传递副本,即临时对象,将临时对象传入func1函数
b=func2(); // 赋值,不进入拷贝构造
cout<<"x = "<<b.getX()<<" "<<"y = "<<b.getY()<<endl;
cout<<"i = "<<i<<" "<<"j = "<<j<<endl;
return 0;
}
// 对象做参数
void func1(Point p){
cout<<"x = "<<p.getX()<<" "<<"y = "<<p.getY()<<endl;
}
// 对象做返回值
Point func2(){
Point a(1,2);
return a;
}
运行结果为:
第1次进入构造函数
第1次进入拷贝构造函数
第2次进入拷贝构造函数
x = 4
y = 5
第2次进入构造函数
i = 2
j = 2
July.4.2021:
四、类的组合
由已有的类组合产生新的类,已有的类为部件类,新产生的类为组合类。组合类不能访问部件类的私有成员。另外,初始化时,由组合类的构造函数负责将部件初始化需要的参数传递给它,由部件的构造函数负责初始化部件类对象。
// 声明形式
类名::类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),……{
// 函数体其他语句
}
类组合的构造函数,不仅负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
(1)构造组合类对象时的初始化次序
- 对初始化列表中的成员,按照成员在类体中定义的次序初始化。
- 对象成员构造函数调用顺序:按对象成员的定义顺序,先声明者先构造。
(2)先处理初始化列表,再执行构造函数的函数体。
July.14.2021:
#include <iostream>
using namespace std;
//CPU类
enum class CPU_Rank{P1=1,P2,P3,P4,P5,P6,P7};
class CPU{
private:
CPU_Rank rank;
int frequency;
float voltage;
public:
CPU(CPU_Rank r,int f,float v):rank(r),frequency(f),voltage(v){cout<<"构造->CPU"<<endl;}
~CPU(){cout<<"析构->CPU"<<endl;}
CPU_Rank Get_ran() const { return rank; }
int GetFrequency() const { return frequency; }
float GetVoltage() const { return voltage; }
void SetRank(CPU_Rank r) { rank = r; }
void SetFrequency(int f) { frequency = f; }
void SetVoltage(float v) { voltage = v; }
void Run() {cout << "CPU运行..." << endl; }
void Stop() {cout << "CPU停止!" << endl; }
};
//内存类
enum RamType{DDR2=2,DDR3,DDR4};
class Ram{
private:
RamType type;
unsigned int frequency;
unsigned int size;
public:
Ram(RamType r,unsigned int f,unsigned int s):type(r),frequency(f),size(s) {cout<<"构造->RAM"<<endl;}
~Ram() {cout<<"析构->RAM"<<endl;}
void SetType(RamType t) { type=t; }
void SetFrequency(unsigned int f) { frequency=f; }
void SetSize(unsigned int s) { size=s;}
RamType GetType() {return type;}
unsigned int GetFrequency() {return frequency;}
unsigned int GetSize() {return size;}
void Run() {cout << "RAM运行..." << endl; }
void Stop() {cout << "RAM停止! " << endl; }
};
//存储类
enum CDROM_InterFace {SATA,USB};
enum CDROM_Install_Type {external,built_in};
class CD_Rom{
private:
CDROM_InterFace interface_type;
unsigned int cache_size;
CDROM_Install_Type install_type;
public:
CD_Rom(CDROM_InterFace infa,unsigned int cs,CDROM_Install_Type inst):interface_type(infa),cache_size(cs),install_type(inst){
cout<<"构造->CD_ROM"<<endl;
}
~CD_Rom(){cout<<"析构->CD_ROM"<<endl;}
void SetInterFace(CDROM_InterFace infa) { interface_type=infa; }
void SetCacheSize(unsigned int cs) { cache_size=cs; }
void SetInstallType(CDROM_Install_Type inst) { install_type =inst; }
CDROM_InterFace GetInterFace() {return interface_type;}
unsigned int GetCacheSize() {return cache_size;}
CDROM_Install_Type GetInstallType() {return install_type;}
void Run() {cout << "ROM运行..." << endl; }
void Stop() {cout << "ROM停止! " << endl; }
};
//计算机类
class Computer{
private:
CPU myCPU;
Ram myRam;
CD_Rom myRom;
unsigned int storage_size;
unsigned int bandwidth;
public:
Computer(CPU cpu,Ram ram,CD_Rom rom,unsigned int stsi,unsigned int bw):myCPU(cpu),myRam(ram),myRom(rom){
storage_size=stsi;
bandwidth=bw;
cout<<"构造->PC"<<endl;
}
~Computer(){cout<<"析构->PC"<<endl;}
void Run(){
myCPU.Run();
myRam.Run();
myRom.Run();
cout<<"PC 运行..."<<endl;
}
void Stop(){
myRam.Stop();
myRom.Stop();
myCPU.Stop();
cout << "PC 停止! " << endl;
}
};
int main(int argc, const char * argv[]) {
CPU a(CPU_Rank::P6,300,2.8);//在枚举类体外使用时,前部要带域
a.Run();
a.Stop();
cout<<"-------------------"<<endl;
Ram b(DDR3,600,64);
b.Run();
b.Stop();
cout<<"-------------------"<<endl;
CD_Rom c(SATA,1024,built_in);
c.Run();
c.Stop();
cout<<"-------------------"<<endl;
Computer d(a,b,c,128,10);
d.Run();
d.Stop();
return 0;
}
运行结果:
构造->CPU
CPU运行...
CPU停止!
构造->RAM
RAM运行...
RAM停止!
构造->CD_ROM
ROM运行...
ROM停止!
构造->PC
析构->CD_ROM
析构->RAM
析构->CPU
CPU运行...
RAM运行...
ROM运行...
PC 运行...
RAM停止!
ROM停止!
CPU停止!
PC 停止!
析构->PC
析构->CD_ROM
析构->RAM
析构->CPU
析构->CD_ROM
析构->RAM
析构->CPU