C++(14)--面向对象、类、构造、析构、this

本文介绍了面向对象编程的基本概念,包括类、对象、构造函数和析构函数。通过地主类的例子,阐述了类的声明、实现以及成员变量的访问修饰符。详细讲解了构造函数的默认构造、一般构造和析构函数的作用,强调了构造函数在对象创建时的重要性。此外,还讨论了this指针在成员函数中的应用,展示了如何通过this指针访问和修改对象的成员。
摘要由CSDN通过智能技术生成

-------------简单的事情重复做,重复的事情用心做,用心的事情坚持做(老九君)---------------

Tips:

  1. 类成员:变量 和 函数 均为成员

  2. .h中声明类,.cpp文件中实现类

  3. .hpp 一般包含实现的内联函数,用于模版类声明与实现共存的情况;建议只要不是纯模版,使用.h 作为头文件, .cpp 作为函数的实现文件

  4. 对象声明:LandOwnerV1 landOwner1; // 声明了一个LandOwner1类型对象landowner1

  5. 构造函数调用,对于无参构造函数不能加括号,因为c++会将其视为函数声明,如int test()

  6. 定义 对象、对象指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
 
ListNode dummy(0, head);
ListNode* dummy_p = new ListNode(0, head);

1. 面向对象 概况

面向对象编程 (oop: object oriented programming)–基于对象的概念,以对象为中心,以类和继承为构造机制,来认识、理解、刻画客观世界;涉及构建相应的软件系统(模拟现实)

NOTE:面向对象不是某一种语言的特性,而是一种编程思想。对象:有 数据 和 容许的操作 组成的封装体,与客观实体有直接的对应关系(属性和方法的集合)

面向对象 Vs 面向过程 (举个栗子)斗地主游戏的开发

  1. 面向过程:一步一步来,很多很多的过程函数:开始游戏-洗牌-发牌-显示手牌…-输出结果。
  2. 面向对象:
    1. 游戏参与者,行为模式是相同的–玩家对象,相同的属性和行为;
    2. 进行游戏的场景–牌桌对象,负责现实游戏的界面及内容;
    3. 游戏规则系统–裁判对象,负责判定牌面、输赢;

1.面向过程:首先考虑要遵循的步骤,然后考虑如何表示这些数据。面向过程代码超过10W行就会难管理,原来有因为飞机控制程序中一个",“写成”."号造成的空难。
2.面向过程:首先会考虑数据,包括数据的表示和数据的使用

2. 类、对象 class、struct

面向对象编程的流程:抽象 -> 用类封装

  1. 抽象:从具体事物中抽取共同的本质特征(处理复杂问题的技巧–简化、抽象)
  2. 用类封装:将抽象转换为用户定义类型的工具,将 数据 和 操作数据的方法 组合成一个整体。
(抽象)地主对象:外表特征--胖,留两撇胡子,两颗大金牙;行为特点--先出牌,多摸三张牌
(用类封装)地主类:
         成员变量:名称、积分、手牌
         成员函数:摸牌、出牌、产看积分

类和对象的关系:类实例就是一个对象;对象集合就是类,(或者说)类就是对象模版,

类的声明:使用class/struct 关键字声明

class 类名{};   // class声明的类 默认成员是私有的,推荐使用class声明类;
struct 类名{};  // struct声明的类 默认成员是共有的,struct声明结构,只包含数据POD,老式数据

c/c++ pod类型介绍 plain old data

地主类 - 类与对象 声明、实现

// 类、对象demo1. LandOwnerV1.cpp 文件中既声明又实现,main.cpp文件中调用
//  类、对象demo1. LandOwnerV1.cpp  地主类的声明、实现
#include <iostream>
using namespace std;

class LandOwnerV1 {
    private:
        string name;	// 名称
        long score;		// 积分
        int cards[20];	// 手牌数组
    public :
        LandOwnerV1() {};	// 默认构造函数
        ~LandOwnerV1() {};	// 默认析构函数
        
        void TouchCard(int CardCount) {   // 暂时省略函数实现
            cout << name << "摸了" << CardCount << "张牌" << endl;
        }
        void ShowScore() {
            cout << name << "当前的积分为:" << score << endl;
        }
};

//  类、对象demo1. mian.cpp 地主类的调用
#include "LandOwnerV1.cpp"

int main() {
	LandOwnerV1 landOwner1; // 声明了一个LandOwner1类型的对象landowner1
	// landOwner1.cards[0] = 0; // 报错:'cards' is a private member of 'LandOwnerV1' ,调用对象成员, 不能直接使用对象的私有成员
	landOwner1.TouchCard(100);
	return 0;
}

//  类、对象demo1. 运行main.cpp 输出
摸了100张牌
// 类、对象demo2. LandOwnerV2.h中声明,LandOwnerV2.cpp中实现,main.cpp文件中调用
// 类、对象demo2. LandOwnerV2.h 地主类的声明
#include <iostream>
using namespace std;

class LandOwnerV2 {
    private:
        long score;		// 积分
        int cards[20];	// 手牌数组
    public :
        string name;	// 名称
        LandOwnerV2();	// 构造函数声明
        ~LandOwnerV2();	// 析构函数声明 

        void TouchCard(int);		// 声明摸牌函数
        void PlayCard(int);		    // 声明出牌函数
        void ShowScore();			// 声明产看积分函数
};

// 类、对象demo2. LandOwnerV2.cpp 地主类的实现
#include <iostream>
#include "LandOwnerV2.h"
using namespace std;

LandOwnerV2::LandOwnerV2() {
    // ctor(constructor)
}

void LandOwnerV2::TouchCard(int CardCount) {
    cout << name << "摸了" << CardCount << "张牌" << endl;
}

void LandOwnerV2::ShowScore() {
    cout << name << "当前的积分为:" << score << endl;
}

LandOwnerV2::~LandOwnerV2() {
    // dtor
}

// 类、对象demo2. mian.cpp 地主类的调用
#include "LandOwnerV2.h"  // 关注.h 文件
using namespace std;
int main() {
	LandOwnerV2 landowner2;
	landowner2.name = "小明";
	//landowner2.TouchCard(20);  // 成员函数没有调用成功
	cout << landowner2.name << endl;
	return 0;
}

3. 访问修饰符 public、private、protected

类成员: 变量 和 函数 均为成员, 不同访问修饰符下成员具备有不同的被访问权限:

  1. public: 修饰的成员 在任意地方都可以访问
  2. private: 修饰的成员 只能在类中或者友元函数中访问, 私有属性可以在名字前面加一个_
  3. protected: 修饰的成员 可以在类中函数、子类函数、友元函数中访问

访问修饰符 放在类声明中,使用 关键字+冒号 的形式表明成员的访问权限,无访问修饰默认为private。

class 类名{
修饰符:
	成员类标;
};

封装: 数据隐藏,不希望别人随意操作, 使用 方法 来实现对成员变量操作的封装。

地主类 - 成员 访问、修改

// 类、对象demo3. 访问修饰符的实验。外部直接修改成员变量,不安全 (引出demo4封装的概念)
// 类、对象demo3. LandOwnerv3.h 文件, Created by 陈莹莹 on 2021/1/28.
#include <iostream>
#ifndef HELLOWORLD_LANDOWNERV3_H
#define HELLOWORLD_LANDOWNERV3_H
using namespace std;
class LandOwnerv3 {
    int cards[20];	// 手牌数组
public :
    string name;
    long score;		// 积分
    LandOwnerv3();	// 构造函数声明, 没有返回值
    ~LandOwnerv3();	// 析构函数声明

    void TouchCard(int);		// 声明摸牌函数
    void PlayCard(int);		    // 声明出牌函数
    void ShowScore(int);	 	// 声明产看积分函数
};
#endif //HELLOWORLD_LANDOWNERV3_H

// 类、对象demo3. LandOwnerv3.cpp 文件, Created by 陈莹莹 on 2021/1/28.
#include <iostream>
#include "LandOwnerv3.h"
using namespace std;
LandOwnerv3::LandOwnerv3() {
    //ctor
}

void LandOwnerv3::ShowScore(int score) {
    cout << name     << "当前的积分为:" << score << endl;
}

LandOwnerv3::~LandOwnerv3() {
    // dtor
}

// 类、对象demo3. mian.cpp 文件
#include <iostream>
#include "LandOwnerv3.h"
using namespace std;
int main(){
    LandOwnerv3 landOwner3;       // 访问修饰符的实验
    landOwner3.name = "巴依老爷";
    landOwner3.score = 100;      // 修改地主积分,非私有成员,会被直接修改
    landOwner3.ShowScore(landOwner3.score);
    return 0;
}

// 类、对象demo3. 运行main.cpp 输出
巴依老爷当前的积分为:100

demo3中操作使得大家可以随意修改score,有一些不合理的积分就会出现。为了解决积分被赋值为不合理的情况,需要将成员变量score进行封装。 使用方法 来实现对成员的封装,数据隐藏、不希望别人随意操作。成员变量常用封装方法get()、set()

// 类、对象demo3改进. 访问修饰符的实验。使用 封装 防止直接修改成员变量
// 类、对象demo3改进. LandOwnerv3.h 文件 Created by 陈莹莹 on 2021/1/28.
#include <iostream>
#ifndef HELLOWORLD_LANDOWNERV3_H
#define HELLOWORLD_LANDOWNERV3_H
using namespace std;
class LandOwnerv3 {
    long score;		// 积分
    int cards[20];	// 手牌数组
public :
    string name;
    LandOwnerv3();	// 构造函数声明, 没有返回值
    ~LandOwnerv3();	// 析构函数声明

    void TouchCard(int);		// 声明摸牌函数
    void PlayCard(int);		    // 声明出牌函数
    void ShowScore();			// 声明产看积分函数
    
    void SetScore(long lScore) {   // 定义成内联函数即可 (?)
        if(lScore < 0) {        // 通过条件判断封装了score的赋值过程
            score = 0;
        } else {
            score = lScore;
        }
    }
};
#endif //HELLOWORLD_LANDOWNERV3_H

// 类、对象demo3改进. LandOwnerv3.cpp 文件  Created by 陈莹莹 on 2021/1/28.
// ........ 如 '类、对象demo3'

// 类、对象demo3改进. mian.cpp 文件
#include <iostream>
#include "LandOwnerv3.h"
using namespace std;
int main(){

    LandOwnerv3 landOwner3;
    landOwner3.name = "巴依老爷";
    landOwner3.SetScore(-100);    // 修改地主积分
    landOwner3.ShowScore();
    return 0;
}

// 类、对象demo3改进. 运行main.cpp 输出
巴依老爷当前的积分为:0
// 类、对象demo4. 访问修饰符的实验。 进一步封装:设置/获取名字,修改积分(版本4)
// LandOwnerv41.h 文件 Created by 陈莹莹 on 2021/1/29.
#ifndef HELLOWORLD_LANDOWNERV41_H
#define HELLOWORLD_LANDOWNERV41_H

#include <iostream>
using namespace std;
class LandOwnerv41 {
    long score;		// 积分
    int cards[20];	// 手牌数组
    string name;
public:
    LandOwnerv41();	    // 构造函数声明, 没有返回值
    ~LandOwnerv41();	// 析构函数声明

    void TouchCard(int);		// 声明摸牌函数
    void PlayCard(int);		    // 声明出牌函数
    void ShowScore();			// 声明产看积分函数
    void SetScore(long lScore){
        if(lScore < 0) {
            score = 0;
        } else {
            score = lScore;
        }
    }
    string GetName() {
        return name;
    }
    void SetName(string lName) {
        name = lName;
    }
};
#endif //HELLOWORLD_LANDOWNERV41_H

// 类、对象demo4. LandOwnerv41.cpp 文件 Created by 陈莹莹 on 2021/1/29.
#include "LandOwnerv41.h"
#include <iostream>
using namespace std;
LandOwnerv41::LandOwnerv41() {
    // ctor
}
void LandOwnerv41::ShowScore(){
    cout << name     << "当前的积分为:" << score << endl;
}

LandOwnerv41::~LandOwnerv41() {
    // dtor
}

// 类、对象demo4. mian.cpp 文件
#include <iostream>
#include "LandOwnerv41.h"
using namespace std;
int main(){
    LandOwnerv41 landOwner4;
    landOwner4.SetName("巴依老爷");
    cout << landOwner4.GetName() << endl;
    landOwner4.SetScore(-100);
    landOwner4.ShowScore();
    return 0;
}

4. 构造函数(重点)

构造函数:(给编译器看)编译器在对象被创建时,为对象分配内存空间,并自动调用构造函数以完成 成员初始化。

KeyPoint:构造函数与类同名,没有返回值,种类如下:

  1. 默认构造函数(无参数构造函数)
  2. 一般构造函数(重载构造)
  3. 拷贝构造函数
  4. 转换构造函数
  5. 移动构造函数

C++ 构造函数详解

地主类 - 默认构造(无参构造)

默认构造函数:一个类必有构造函数,即便没有显式定义构造函数,编译器会创建一个默认构造函数,具体形式如下:

ClassName() {};                  // 方式1:xxsx
LandOwnerV4() = default;         // 方式2:显式写法
  1. 如果创建的类中未书写任何构造函数,系统会自动生成默认的无参构造函数(函数为空)按照如下规则初始化数据乘员:
    a. 存在类内初始值,则用该初始值初始化乘员
    b. 否则使用默认初始化该成员
  2. 如果书写了构造函数,系统不会自动生成默认构造函数,如果希望有一个这样的无参数构造函数,需要自己显示书写LandOwnerV4() = default;
struct ListNode() {
    int val = 0;
    ListNode *next = nullptr;                        // 类内初始值 初始化成员
    ListNode(int x) : val{x}, next(nullptr) {}       // 初始值参数列表 初始化成员
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };

note1: 默认构造 这里又称为 无参数构造函数, 其实是不同通过传参列表 确定成员变量的值
note2: 调用无参构造函数,其无参数,不用写(), 否则会被认为是函数声明。

// 类、对象demo4改进. 演示默认构造函数,使用无参构造函 初始化 成员变量
// 类、对象demo4改进. LandOwnerv41.h 文件
// ........ 如'类、对象demo4'

// 类、对象demo4改进. LandOwnerv41.cpp 文件 Created by 陈莹莹 on 2021/1/29.
#include "LandOwnerv41.h"
#include <iostream>
#include <memory.h>
using namespace std;

LandOwnerv41::LandOwnerv41() {
    cout << "LandOwnerV41的无参数构造函数(默认构造)被调用!" << endl;
    name = "默认地主";
    score = 0;    
    memset(cards, 0, sizeof(cards)/sizeof(cards[0])); //将用户的手牌数组初始化为0
    cout << "初始化的结果如下:" << endl;
    cout << "名字:" << name << endl;
    cout << "积分:" << score << endl;
    cout << "手牌数组:" ;
    for(int i=0; i < sizeof(cards)/sizeof(cards[0]); i++) {
        cout << cards[i] << "\t";
    }
    cout << endl;
}

void LandOwnerv41::ShowScore() {
    cout << name     << "当前的积分为:" << score << endl;
}

LandOwnerv41::~LandOwnerv41() {
    //dtor
}

// 类、对象demo4改进. mian.cpp 文件
#include "LandOwnerv41.h"
using namespace std;
int main(){
    LandOwnerv41 landOwnerv4;     // 调用无参构造函数,其无参数,所以不用写()
    LandOwnerv41 landOwnerv4();   // 会被认为是函数声明,不会实现无参构造函数调用,而不会有任何输出。
    return 0;
}

// 类、对象demo4改进. 运行main.cpp 输出  // 手牌数组的初始化不是全为0?
LandOwnerV41的无参数构造函数(默认构造)被调用!
初始化的结果如下:
名字:默认地主
积分:0
手牌数组:0	0	0	0	0	0	0	0	-375207560	32766	-375207584	32766	0	1	0	0	0	0	0	0	

一般构造函数 (重载构造)(带参构造函数),语法:

类名::构造(类型1 参数1, 类型2 参数2, ....){
	// 相关初始化方法
}

学生类 - 一般构造函数(初始值参数列表)、堆内存对象、栈内存对象

一般构造函数:推荐使用 初始值列表的写法,直接初始化数据乘员,而非对数据成员直行赋值操作。( 类、对象demo5中有两种方式写法的对比), 不同参数列表的构造函数,是函数重载的表现。

  1. 栈内存中的对象(类似于函数声明)–具体对象由系统创建并释放,不用担心内存泄露。
    声明周期只在声明区域的大括号内。
    优点 - 栈内存优势 存取速度快(仅次于寄存器),缺点栈内存中的数据大小生存期是确定的,缺乏灵活性。
Student stu;     //  自定义类型名 对象名;
  1. 堆内存中的对象(需要new关键字)p_stu1是指针,必须使用delete释放
    优点:使用灵活可以赋给全局变量,可以把对象作为函数的返回值
Student *p_stu1 = new Student();
Student *p_stu2 = new Student();
auto *p_stu3 = new Student(); // auto 自动类型判断,不推荐使用,sizeof 会报错

NOTE: 类对象推荐放在堆内存中,即使用new来创建对象。
C++ new用法 (结论:别使用不带括号的new。)

Tips: 如果构造函数中只有一个参数,且单参数构造函数只有一个,可以直接使用赋值操作进行初始化

// 类、对象demo5. 学生类 - 堆内存对象、栈内存对象
// 类、对象demo5. Student.h 文件 Created by 陈莹莹 on 2021/2/2.
#ifndef HELLOWORLD_STUDENT_H
#define HELLOWORLD_STUDENT_H
#include <iostream>
using namespace std;

class Student {
private:
    string m_name;
    string m_desc;
    int m_age;
public:
    Student();          // 默认构造函数
    Student(string, string);
    ~Student();
    void showInfo();

};
#endif //HELLOWORLD_STUDENT_H

// 类、对象demo5. Student.cpp 文件  Created by 陈莹莹 on 2021/2/2.
#include <iostream>
#include "Student.h"
using namespace std;

Student::Student() {
    cout << "默认构造函数" << endl;
}

// 语法正确 但是 草率,通过赋值操作实现成员函数值的设置
//Student::Student(string name, string desc) {
//    m_name = name;
//    m_desc = desc;
//    cout << "设置带参构造" << endl;
//}

// 推荐使用:初始值参数列表 的写法
Student::Student(string name, string desc):m_name(name), m_desc(desc) {
    cout << "设置带参构造" << endl;    // 其实应该是一个空函数体
}

void Student::showInfo() {
    cout << m_desc << m_name << endl;
}

Student::~Student() {
}

// 类、对象demo5. mian.cpp 文件
#include <iostream>
#include "Student.h"
using namespace std;
int main(){
    Student stu1;                        // 演示构造函数,实质就是函数重载
    Student stu2("马化腾","普通家庭");     // 在栈内存中直接分配空间,速度块
    // Student stu4 = 45                // 如果构造函数中只有一个参数,且单参数构造函数只有一个,可以直接使用赋值操作进行初始化
    stu2.showInfo();
    Student * stu5 = new Student("杰克马", "毁创阿里");  // 使用new分配堆内存, 学生类指针
    stu5 ->showInfo();                    // 类指针访问方法要使用 ->
    return 0;
}

// 类、对象demo5. 输出结果
默认构造函数
设置带参构造
普通家庭马化腾           // stu2.showInfo();   的输出
设置带参构造
毁创阿里杰克马           // stu5 ->showInfo(); 的输出

5. 析构函数

析构函数:特殊成员函数,对象销毁时自动调用,用来完成清理工作。
语法:每个类只能有一个析构函数、函数名为‘~类名’、没有参数
析构函数体:需要释放(delete)掉在构造函数中手动分配(new)的空间(对象内部的成员变量)。

  1. 析构函数用来释放对象使用的资源,并销毁对象的非static数据成员。
  2. 无论何时一个对象被销毁,都会自动调用析构函数(隐式析构)。

栈内存对象:在栈内存中构造的对象,在栈区(mian函数)销毁时,类对象会被自动销毁。
堆内存对象:在堆内存中构建的类对象,需要手动释放。

学生类 - 析构函数

类对象推荐放在堆内存中,即使用new来创建对象。

// 类、对象demo6. 学生类 - 析构函数演示(构造函数中未new资源)
// 类、对象demo6. Student.h 文件
// ........ 如'类、对象demo5'

// 类、对象demo6. Student.cpp 文件 Created by 陈莹莹 on 2021/2/2.
#include <iostream>
#include "Student.h"
using namespace std;

Student::Student() {
    cout << "默认构造函数" << endl;
}
//Student::Student(string name, string desc) {
//    // 参数列表不同,函数重载
//    m_name = name;
//    m_desc = desc;
//    cout << "设置带参构造" << endl;
//}

//初始化参数列表的写法,和上面的带参数构造方法完全相同
Student::Student(string name, string desc):m_name(name), m_desc(desc) {
    cout << "设置带参构造" << endl;
};

void Student::showInfo() {
    cout << m_desc << m_name << endl;
}

Student::~Student() {
    cout << m_name << "被释放" << endl;
}

// 类、对象demo6. mian.cpp 文件
#include <iostream>
#include "Student.h"
using namespace std;
int main(){
    Student stu1;
    Student stu2("马化腾","普通家庭");     // 在栈内存中直接分配空间,速度块
    // Student stu4 = 45                 // 如果构造函数中只有一个参数,且单参数构造函数只有一个,可以直接使用赋值操作进行初始化
    stu2.showInfo();
    // 学生指针类型, 使用new 分配内存空间
    Student * stu5 = new Student("杰克马", "毁创阿里");  // 使用new分配堆内存, 学生类指针
                                       // 类指针访问方法要使用 ->
    stu5 ->showInfo();
    return 0;
}

// 类、对象demo6. 输出结果
默认构造函数     // stu1
设置带参构造     // stu2
普通家庭马化腾   // stu2.showInfo()
设置带参构造     // stu5
毁创阿里杰克马   // stu5.showInfo()
杰克马被释放     // main函数执行结束,自动释放 杰克马
马化腾被释放     // main函数执行结束,自动释放 马化腾
被释放          // main函数执行结束,自动释放 stu1

6. this指针(重点)

(类似于于python中的self,在python中显式传递self参数)
每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象,可以通过this关键字访问当前对象的成员。this 在成员函数执行前就已经创建,在析构函数之后被销毁。

  1. 访问成员变量:this -> 成员名;
  2. 访问成员函数:this -> 函数名();
  3. this 指针的类型为*const,为右值;(可以赋值)
  4. this指针本身不占用大小,并不是对象的一部分,用sizeof 测不出来大小
  5. this指针作用域在成员函数内部;
  6. this指针是类成员函数的第一个默认隐含参数,编译器自动维护传递,编写者不能显式传递。
  7. 在类非静态成员函数中才可以使用this指针,其他任何函数都不可以(static是先于类实例存在的,this还没有创建)

学霸类 - 返回对象的引用

// 类、对象demo7. 学生类 - 
// 类、对象demo7. Student.h  Created by 陈莹莹 on 2021/2/2.
#ifndef HELLOWORLD_STUDENT_H
#define HELLOWORLD_STUDENT_H
#include <iostream>
using namespace std;

class Student {
private:
    string m_name;
    string m_desc;
    int m_age;
    float *scores;        // 学生的分数数组,在某个构造的时候进行初始化
    int scoreCount;      // 学生成绩的个数

public:
    Student();          // 默认构造函数
    Student(string, string);
    ~Student();
    void showInfo();
    void InitScores();                     // 初始化学生的成绩数组,默认分配一个元素空间
    void AddScore(float score);
    // 返回值是引用时,是非常危险的。this可以直接修改属性,建议在函数声明处加一个const, 限制在该函数内部不能通过this指针修改属性,太麻烦了,要该的地方太多了 todo
    Student & GetSuperScholars(Student &); //返回学霸对象 todo 
    float GetTotal();                     // 参数处加const 保证不对该参数进行修改。todo 
    string GetName() {return m_name;} 

};

#endif //HELLOWORLD_STUDENT_H

// 类、对象demo7. Student.cpp  Created by 陈莹莹 on 2021/2/2.
#include <iostream>
#include "Student.h"
using namespace std;

Student::Student() {
    cout << "默认构造函数" << endl;
    InitScores();
}

Student::Student(string name, string desc):m_name(name), m_desc(desc) {
    cout << "设置带参构造" << endl;
    InitScores();
};

void Student::InitScores() {
    this -> scores = new float [1];     // 构造函数中 new 出来的成员变量,析构函数中需要释放
    this -> scoreCount = 1;
}

void Student::showInfo() {
    cout << m_desc << m_name << endl;
    for(int i = 0; i < scoreCount-1; i++){
        cout << this->scores[i] << "\t";
    }
    cout << endl;
}

void Student::AddScore(float score) {
    // 1. 创建一个新数组,分配scoreCount+1 个空间
    // 2. 复制愿数组中的内容到新数组中
    // 3. scoreCount++
    // 4. scores指向新数组
    this -> scores[this->scoreCount - 1] = score;    // 初始化的时候分配了空间,未赋值,现在赋后拷贝到空间增加了的内存中
    float *newScores = new float [scoreCount + 1];
    float *oldScores = scores;
    memcpy(newScores, scores, sizeof(float) * scoreCount);
    scoreCount++;
    scores = newScores;
    delete oldScores;    // 删除原来的指针
}

Student& Student::GetSuperScholars(Student &otherstu){
    // otherstu 要对比的另一学生对象
    // 返回总分比较大的那个学生对象
    // 分别计算两个学生的总分
    if(this->GetTotal() > otherstu.GetTotal()){
        return *this;       // 指针, * 得引用。
    } else {
        return otherstu;    // 直接返回引用
    }
}

float Student::GetTotal() {
    float sum = 0;
    for(int i = 0; i < scoreCount; i++){
        sum += scores[i];
    }
    return sum;
}

Student::~Student() {
    cout << m_name << "被释放" << endl;
    delete this -> scores;
}

// 类、对象demo7. mian.cpp
#include <iostream>
#include "Student.h"
using namespace std;
int main(){
    Student *ptr_stu1 = new Student("迪丽热巴","微胖女孩");   // 堆内存对象
    ptr_stu1->AddScore(78.9);
    ptr_stu1->AddScore(77.9);
    ptr_stu1->AddScore(72.9);
    ptr_stu1->AddScore(79.9);
    ptr_stu1->AddScore(800);
    ptr_stu1->showInfo();
    Student stu2("刘强东","不爱美人");                       // 栈内存对象
    stu2.AddScore(78.9);
    stu2.AddScore(77.9);
    stu2.AddScore(72.9);
    stu2.AddScore(79.9);
    stu2.AddScore(90);
    stu2.showInfo();

    Student &scholar1 = stu2.GetSuperScholars(*ptr_stu1);  //返回迪丽热巴的引用,
    Student &scholar2 = ptr_stu1->GetSuperScholars(stu2);  //返回迪丽热巴的引用
    cout << "学霸是" << scholar1.GetName() << "\t" << scholar2.GetName() << endl;
    delete ptr_stu1;       // scholar2 栈内存中定义的,main 结束后会自动释放
    return 0;

}

// 类、对象demo7. 输出结果
设置带参构造
微胖女孩迪丽热巴
78.9	77.9	72.9	79.9	800	
设置带参构造
不爱美人刘强东
78.9	77.9	72.9	79.9	90	
学霸是迪丽热巴	迪丽热巴
迪丽热巴被释放
刘强东被释放

小结:类就是自己定义的数据类型,对象就是变量

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值