C++:“类”的相关问题学习

July.2.2021:

一、第一个类程序。

首次接触面向对象的类(class),感觉有点像C语言的结构体(struct),但是不同。有如下区别:

  1. 类具有权限管理,默认为私有变量。而结构体是公有变量。
  2. 类可以有函数成员,而结构体不能。而且函数成员可以是重载函数和带默参数值的函数。
#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)定义构造函数

  • 语法规则为:
  1. 函数名与类名相同;
  2. 不能定义返回值类型,也不能有return语句;
  3. 可以有形参,也可以没有;
  4. 可以内联、重载、带默认值。
  • 调用方式:不用显式调用,而是对象被创建时自动调用。

⚠️以下不是正确的函数重载形式,不能一同出现在类中
//默认构造函数:调用时不需要实参的构造函数
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


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容