6.构造函数

6.面向对象-封装-构造函数


6.1 知识点

注意事项

  • 在 C++ 中,结构体中的构造函数规则和类中基本一致
  • 由于我们之前已经学习过结构体构造函数
  • 因此本节课,我们主要用相关知识对类的构造函数进行一个快速讲解即可

构造函数

基本概念

构造函数是特殊的成员函数,用于对类对象进行初始化。当声明类变量(对象)时,会自动调用构造函数。

基本规则

  • 没有返回值
  • 函数名必须与类名相同
  • 可以被重载
  • 两种赋值方式
    • 在函数中赋值
    • 利用成员初始化列表初始化(更高效)
  • 处理参数与成员变量重名
    若参数与成员变量重名,可使用 this->成员变量 指代类的成员变量。

调用构造函数的几种方式

#pragma once
// Lesson06类展示了C++中构造函数的重载和委托机制
class Lesson06
{
public:
    // 默认构造函数,将所有成员初始化为0
    Lesson06();

    // 带单个参数的构造函数,初始化x,y和z为0
    Lesson06(int x);

    // 带两个参数的构造函数,委托给单参数构造函数初始化x
    Lesson06(int x, int y);

    // 带三个参数的构造函数,使用成员初始化列表初始化所有成员
    Lesson06(int x, int y, int z);

public:
    // 成员变量声明并提供默认初始化值
    // 注意:如果构造函数中进行了初始化,这些值将被覆盖
    int x = 11;
    int y = 12;
    int z = 13;
};
#include "Lesson06.h"
#include <iostream>
using namespace std;

// 默认构造函数实现
// 功能:将所有成员变量初始化为0
Lesson06::Lesson06()
{
    x = 0;
    y = 0;
    z = 0;
}

// 单参数构造函数实现
// 参数x:用于初始化成员变量x
// 行为:输出构造信息,初始化x,y和z为0
Lesson06::Lesson06(int x)
{
    cout << "Lesson06 一个参数" << endl;
    this->x = x;  // 使用this指针区分参数和成员变量
    y = 0;
    z = 0;
}

// 双参数构造函数实现 使用委托构造函数
// 参数x, y:分别初始化成员变量x和y
// 行为:委托单参数构造函数初始化x,再初始化y
Lesson06::Lesson06(int x, int y) :Lesson06(x)  // 委托构造
{
    cout << "Lesson06 两个参数" << endl;
    this->y = y;  // 初始化委托构造未处理的成员
}

// 三参数构造函数实现
// 以下两种写法效果等价
// 写法1:使用初始化列表(推荐) 更清晰地表达成员变量的初始化逻辑
// 参数x, y, z:分别初始化对应的成员变量 在对象创建时立即初始化成员变量
// 行为:使用成员初始化列表直接初始化所有成员
Lesson06::Lesson06(int x, int y, int z) :x(x), y(y), z(z)
{
    // 空函数体,初始化已在初始化列表完成
}
// 写法2:在构造函数体中赋值
//Lesson06::Lesson06(int x, int y, int z) {
//    this->x = x;  // 先默认初始化成员变量,再赋值
//    this->y = y;
//    this->z = z;
//}
//3.调用构造函数的几种方式
//  3-1.栈上分配-不带new
//      3-1-1.类型 类对象(参数...)
Lesson06 l;
cout << l.x << endl;//0
cout << l.y << endl;//0
cout << l.z << endl;//0

Lesson06 l2(10);
cout << l2.x << endl;//10
cout << l2.y << endl;//0
cout << l2.z << endl;//0

Lesson06 l3(10, 11);
cout << l3.x << endl;//10
cout << l3.y << endl;//11
cout << l3.z << endl;//0

Lesson06 l4(10, 11, 12);
cout << l4.x << endl;//10
cout << l4.y << endl;//11
cout << l4.z << endl;//12

//      3-1-2.类型 类对象{参数...}
Lesson06 l5{ 1 };

//      3-1-3.类型 类对象 = 类型(参数...);
Lesson06 l6 = Lesson06(1, 2);

//      3-1-4.类型 类对象 = 类型{参数...};
Lesson06 l7 = Lesson06{ 1, 2 };

//      3-1-5.类型 类对象 = {参数...};
Lesson06 l8 = { 1, 2 };

//  3-2.堆上分配-带new
//      类型* 类对象 = new 类型(参数);
Lesson06* l9 = new Lesson06(3, 4);
delete(l9);
l9 = nullptr;

默认的构造函数

//4.如果我们没有为结构体定义任何构造函数
//  编译器会生成一个默认的构造函数
//  如果定义了其他构造函数(如带参数的构造函数)
//  则默认构造函数不会被自动生成
//  除非我们显式定义它
//  注意:默认构造函数就是一个无参的构造函数
Lesson06_2 l_2;// 调用默认无参构造函数
// 成员变量初始化:
// x = 0(使用类内初始值)
// ptr = nullptr(使用类内初始值)

Lesson06_2* l_2_2 = new Lesson06_2();

拷贝构造函数和移动构造函数

#pragma once
class Lesson06_2
{
    // 未声明任何构造函数,编译器会自动生成以下默认构造函数:
    // 1. 默认无参构造函数
    // Lesson06_2();

    // 2. 拷贝构造函数
    // Lesson06_2(const Lesson06_2& other);

    // 3. 移动构造函数(C++11 起)
    // Lesson06_2(Lesson06_2&& other) noexcept;

    // 4. 拷贝赋值运算符
    // Lesson06_2& operator=(const Lesson06_2& other);

    // 5. 移动赋值运算符(C++11 起)
    // Lesson06_2& operator=(Lesson06_2&& other) noexcept;

    // 6. 析构函数
    // ~Lesson06_2();

    // 手动实现构造函数
public:
    Lesson06_2();                              // 声明默认构造
    Lesson06_2(const Lesson06_2& other);       // 声明拷贝构造
    Lesson06_2(Lesson06_2&& other) noexcept;   // 声明移动构造
    ~Lesson06_2();

public:
    int x = 0;
    int* ptr = nullptr; // 假设这是一个动态资源
};
#include "Lesson06_2.h"
#include <iostream>
using namespace std;

Lesson06_2::Lesson06_2()
{
    cout << "无参构造" << endl;
}

// 拷贝构造函数实现 需要正确实现深拷贝
Lesson06_2::Lesson06_2(const Lesson06_2& other) : x(other.x) {
    if (other.ptr) {
        ptr = new int(*other.ptr);
    }
    else {
        ptr = nullptr;
    }
}

// 移动构造函数实现
// noexcept 是 C++11 引入的异常规范,表明函数不会抛出异常。
//
Lesson06_2::Lesson06_2(Lesson06_2&& other) noexcept : x(other.x), ptr(other.ptr) {
    other.ptr = nullptr;
}

Lesson06_2::~Lesson06_2()
{
    delete ptr; // 释放动态分配的内存
    ptr = nullptr;
}
//  5-1.传入 const 左值引用的构造(拷贝构造)
//      主要作用:避免不必要的复制,允许使用已有的对象用来初始化成员
//      一般用来处理复制另一个对象的数据
Lesson06_2 obj1;
obj1.x = 10;
obj1.ptr = new int(20); // 假设分配了动态资源
Lesson06_2 obj2 = obj1; // 调用拷贝构造函数
// 默认行为(浅拷贝):
// obj2.x = obj1.x;
// obj2.ptr = obj1.ptr; // 危险!两个对象指向同一块内存

//潜在问题与解决方案 
//重复释放可能导致崩溃
//delete obj1.ptr; // 释放第一次 其实也不需要释放 析构函数会释放
//delete obj2.ptr; // 重复释放,导致崩溃!
//所以需要再拷贝构造函数中正确实现深拷贝

//  5-2.传入 右值引用的构造(移动构造)
//      主要作用:接收将要被移动的临时对象或其他右值,可以提高性能

Lesson06_2 createObject() {
    return Lesson06_2(); // 返回临时对象
}



Lesson06_2 obj3 = createObject(); // 调用移动构造函数
// 默认行为(浅拷贝+资源转移):
// obj3.x = 临时对象.x;
// obj3.ptr = 临时对象.ptr;//将源对象的指针赋值给新对象
// 临时对象.ptr = nullptr; // 源对象资源被置空 这是在移动构造函数中写的 不写不会有

// 如果不执行 other.ptr = nullptr
//obj3.ptr 和源对象的 other.ptr 指向同一块内存 
// 临时对象在语句结束后会被销毁,调用析构函数 
// 析构函数会del释放 other.ptr 指向的内存,并将其置为nullptr 
// 此时 obj3.ptr 指向的内存已经被释放,变成了野指针
//当 obj3 被销毁时,再次调用析构函数释放 obj3.ptr,会导致双重释放错误

委托构造函数

  • 所谓的委托构造函数
  • 就是允许一个构造函数调用另一个构造函数,以减少代码重复
  • 基本语法:
  构造函数():另一个重载的构造函数(参数)
  {}
  • 它的主要作用,说人话:
    在调用某一个构造函数之前可以先调用另一个构造函数,复用相同代码。

  • 双参数构造函数实现 使用委托构造函数

    • 参数 x, y:分别初始化成员变量 x 和 y
    • 行为:委托单参数构造函数初始化 x,再初始化 y
Lesson06::Lesson06(int x, int y) :Lesson06(x)  // 委托构造
{
    cout << "Lesson06 两个参数" << endl;
    this->y = y;  // 初始化委托构造未处理的成员
}

6.2 知识点代码

Lesson06.h

#pragma once
// Lesson06类展示了C++中构造函数的重载和委托机制
class Lesson06
{
public:
    // 默认构造函数,将所有成员初始化为0
    Lesson06();

    // 带单个参数的构造函数,初始化x,y和z为0
    Lesson06(int x);

    // 带两个参数的构造函数,委托给单参数构造函数初始化x
    Lesson06(int x, int y);

    // 带三个参数的构造函数,使用成员初始化列表初始化所有成员
    Lesson06(int x, int y, int z);

public:
    // 成员变量声明并提供默认初始化值
    // 注意:如果构造函数中进行了初始化,这些值将被覆盖
    int x = 11;
    int y = 12;
    int z = 13;
};

Lesson06.cpp

#include "Lesson06.h"
#include <iostream>
using namespace std;

// 默认构造函数实现
// 功能:将所有成员变量初始化为0
Lesson06::Lesson06()
{
    x = 0;
    y = 0;
    z = 0;
}

// 单参数构造函数实现
// 参数x:用于初始化成员变量x
// 行为:输出构造信息,初始化x,y和z为0
Lesson06::Lesson06(int x)
{
    cout << "Lesson06 一个参数" << endl;
    this->x = x;  // 使用this指针区分参数和成员变量
    y = 0;
    z = 0;
}

// 双参数构造函数实现 使用委托构造函数
// 参数x, y:分别初始化成员变量x和y
// 行为:委托单参数构造函数初始化x,再初始化y
Lesson06::Lesson06(int x, int y) :Lesson06(x)  // 委托构造
{
    cout << "Lesson06 两个参数" << endl;
    this->y = y;  // 初始化委托构造未处理的成员
}

// 三参数构造函数实现
// 以下两种写法效果等价
// 写法1:使用初始化列表(推荐) 更清晰地表达成员变量的初始化逻辑
// 参数x, y, z:分别初始化对应的成员变量 在对象创建时立即初始化成员变量
// 行为:使用成员初始化列表直接初始化所有成员
Lesson06::Lesson06(int x, int y, int z) :x(x), y(y), z(z)
{
    // 空函数体,初始化已在初始化列表完成
}
// 写法2:在构造函数体中赋值
//Lesson06::Lesson06(int x, int y, int z) {
//	this->x = x;  // 先默认初始化成员变量,再赋值
//	this->y = y;
//	this->z = z;
//}

Lesson06_2.h

#pragma once
class Lesson06_2
{
    // 未声明任何构造函数,编译器会自动生成以下默认构造函数:
    // 1. 默认无参构造函数
    // Lesson06_2();

    // 2. 拷贝构造函数
    // Lesson06_2(const Lesson06_2& other);

    // 3. 移动构造函数(C++11 起)
    // Lesson06_2(Lesson06_2&& other) noexcept;

    // 4. 拷贝赋值运算符
    // Lesson06_2& operator=(const Lesson06_2& other);

    // 5. 移动赋值运算符(C++11 起)
    // Lesson06_2& operator=(Lesson06_2&& other) noexcept;

    // 6. 析构函数
    // ~Lesson06_2();

//手动实现构造函数
public:
    Lesson06_2();//声明默认构造
    Lesson06_2(const Lesson06_2& other);      // 声明拷贝构造
    Lesson06_2(Lesson06_2&& other) noexcept; // 声明移动构造
    ~Lesson06_2();


public:
    int x = 0;
    int* ptr = nullptr; // 假设这是一个动态资源
};

Lesson06_2.cpp

#include "Lesson06_2.h"
#include <iostream>
using namespace std;

Lesson06_2::Lesson06_2()
{
    cout << "无参构造" << endl;
}

// 拷贝构造函数实现 需要正确实现深拷贝
Lesson06_2::Lesson06_2(const Lesson06_2& other) : x(other.x) {
    if (other.ptr) {
        ptr = new int(*other.ptr);
    }
    else {
        ptr = nullptr;
    }
}

// 移动构造函数实现
//noexcept 是 C++11 引入的异常规范,表明函数不会抛出异常。
//
Lesson06_2::Lesson06_2(Lesson06_2&& other) noexcept : x(other.x), ptr(other.ptr) {
    other.ptr = nullptr;
}

Lesson06_2::~Lesson06_2()
{
    delete ptr; // 释放动态分配的内存
    ptr = nullptr;
}

Lesson06_面向对象_封装_构造函数.cpp

#include <iostream>
#include "Lesson06.h"
#include "Lesson06_2.h"
using namespace std;


// 添加函数声明(原型)
Lesson06_2 createObject();

int main()
{
    #pragma region 注意事项
    //在C++中,结构体中的构造函数规则和类中基本一致
    //由于我们之前已经学习过结构体构造函数
    //因此本节课,我们主要用相关知识对类的构造函数进行一个快速讲解即可
    #pragma endregion

    #pragma region 知识点一 构造函数(结构体中讲解过的内容)
    //1.基本概念:
    //	构造函数是一个特殊的成员函数
    //	是用来对类对象进行初始化用的
    //	当我们声明一个类变量(对象)时,会自动调用构造函数

    //2.基本规则:
    //	2-1.没有返回值
    //	2-2.函数名必须和类名相同
    //	2-3.可以被重载
    //	2-4.两种赋值方式
    //		2-4-1:在函数中赋值
    //		2-4-2:利用成员初始化列表来初始化(更高效)
    //	2-5.若传入参数和成员变量重名,可以使用 this->成员变量 来表示是成员变量

    //3.调用构造函数的几种方式
    //	3-1.栈上分配-不带new
    //		3-1-1.类型 类对象(参数...)

    Lesson06 l;
    cout << l.x << endl;//0
    cout << l.y << endl;//0
    cout << l.z << endl;//0

    Lesson06 l2(10);
    cout << l2.x << endl;//10
    cout << l2.y << endl;//0
    cout << l2.z << endl;//0

    Lesson06 l3(10, 11);
    cout << l3.x << endl;//10
    cout << l3.y << endl;//11
    cout << l3.z << endl;//0

    Lesson06 l4(10, 11, 12);
    cout << l4.x << endl;//10
    cout << l4.y << endl;//11
    cout << l4.z << endl;//12

    //		3-1-2.类型 类对象{参数...}
    Lesson06 l5{ 1 };

    //		3-1-3.类型 类对象 = 类型(参数...);
    Lesson06 l6 = Lesson06(1, 2);

    //		3-1-4.类型 类对象 = 类型{参数...};
    Lesson06 l7 = Lesson06{ 1, 2 };

    //		3-1-5.类型 类对象 = {参数...};
    Lesson06 l8 = { 1, 2 };

    //	3-2.堆上分配-带new
    //		类型* 类对象 = new 类型(参数);
    Lesson06* l9 = new Lesson06(3, 4);
    delete(l9);
    l9 = nullptr;



    //4.如果我们没有为结构体定义任何构造函数
    //	编译器会生成一个默认的构造函数
    //	如果定义了其他构造函数(如带参数的构造函数)
    //	则默认构造函数不会被自动生成
    //	除非我们显式定义它
    //	注意:默认构造函数就是一个无参的构造函数
    Lesson06_2 l_2;// 调用默认无参构造函数
    // 成员变量初始化:
    // x = 0(使用类内初始值)
    // ptr = nullptr(使用类内初始值)

    Lesson06_2* l_2_2 = new Lesson06_2();


    //5.类中还有两个默认的构造函数


    //	5-1.传入 const 左值引用的构造(拷贝构造)
    //		主要作用:避免不必要的复制,允许使用已有的对象用来初始化成员
    //		一般用来处理复制另一个对象的数据
    Lesson06_2 obj1;
    obj1.x = 10;
    obj1.ptr = new int(20); // 假设分配了动态资源
    Lesson06_2 obj2 = obj1; // 调用拷贝构造函数
    // 默认行为(浅拷贝):
    // obj2.x = obj1.x;
    // obj2.ptr = obj1.ptr; // 危险!两个对象指向同一块内存

    //潜在问题与解决方案 
    //重复释放可能导致崩溃
    //delete obj1.ptr; // 释放第一次 其实也不需要释放 析构函数会释放
    //delete obj2.ptr; // 重复释放,导致崩溃!






    //	5-2.传入 右值引用的构造(移动构造)
    //		主要作用:接收将要被移动的临时对象或其他右值,可以提高性能


    Lesson06_2 obj3 = createObject(); // 调用移动构造函数
    // 默认行为(浅拷贝+资源转移):
    // obj3.x = 临时对象.x;
    // obj3.ptr = 临时对象.ptr;//将源对象的指针赋值给新对象
    // 临时对象.ptr = nullptr; // 源对象资源被置空 这是在移动构造函数中写的
    // 如果不执行 other.ptr = nullptr
    //obj3.ptr 和源对象的 other.ptr 指向同一块内存 
    // 临时对象在语句结束后会被销毁,调用析构函数 
    // 析构函数会释放 other.ptr 指向的内存,并将其置为nullptr 
    // 此时 obj3.ptr 指向的内存已经被释放,变成了野指针
    //当 obj3 被销毁时,再次调用析构函数释放 obj3.ptr,会导致双重释放错误



    #pragma endregion

    #pragma region 知识点二  委托构造函数

    //所谓的委托构造函数
    //就是允许一个构造函数调用另一个构造函数,以减少代码重复
    //基本语法:
    //构造函数():另一个重载的构造函数(参数)
    //{}

    //它的主要作用,说人话:
    //在调用某一个构造函数之前可以通过这种方式先调用另一个构造函数
    //达到复用相同代码的作用,这样我们在一些构造函数中就可以少些一些代码逻辑了

    #pragma endregion

}

Lesson06_2 createObject() {
    return Lesson06_2(); // 返回临时对象
}

6.3 练习题

基于成员方法练习题:对人类的构造函数进行重载,用人类创建若干个对象

#pragma once
#include <iostream>
#include "Food.h"
using namespace std;

//前向声明
//class Food;

class Person
{
public:
    Person();
    Person(string name, float height);
    Person(string name, float height, int age, string homeAddress);

    void Speak(string str);
    void Walk();
    void Eat(Food food);
    int GetMoney()
    {
        return money;
    }

    void SetMoney(int value)
    {
        money = value;
    }
public:
    string name = "韬";
    float height = 1;
    int age = 1;
    string homeAddress = "广州";

private:
    int money = 999;
};
#include "Person.h"
#include "Food.h"

Person::Person()
{
    //这里面什么都没写 那具体成员变量的值 就会使用 在声明赋值时采用的值了
}

Person::Person(string name, float height) : name(name), height(height)
{
    //通过列表形式初始化了两个成员变量
}

Person::Person(string name, float height, int age, string homeAddress) : Person(name, height)
{
    //委托构造函数初始化了两个 复用了代码
    //自己写了两个参数初始化
    this->age = age;
    this->homeAddress = homeAddress;
}

void Person::Speak(string str)
{
    cout << name << "说:" << str << endl;
}

void Person::Walk()
{
    cout << name << "开始走路了" << endl;
}

void Person::Eat(Food food)
{
    cout << name << "开始吃" << food.name << ",获得了" << food.kaLuLi << "能量" << endl;
}
Person p;
cout << p.age << endl;
cout << p.name << endl;
cout << p.homeAddress << endl;
cout << p.height << endl;

Person p2 = Person("韬老师", 10);
cout << p2.age << endl;
cout << p2.name << endl;
cout << p2.homeAddress << endl;
cout << p2.height << endl;

Person p3 = Person("韬老师", 10, 18, "Gay都");
cout << p3.age << endl;
cout << p3.name << endl;
cout << p3.homeAddress << endl;
cout << p3.height << endl;

// 输出:
// 1
// 韬
// 广州
// 1
// 1
// 韬老师
// 广州
// 10
// 18
// 韬老师
// Gay都
// 10

基于成员变量练习题:对班级类的构造函数进行重载,用班级类创建若干个对象

#pragma once
#include <iostream>
using namespace std;

//前向声明 告诉编译器 有这两个类 具体实现 先不管
class Person;
class Student;

class Food
{
public:
    string name;
    int kaLuLi;

public:
    void BeiEat(Person p);
    void BeiEat(Student s);
};
#pragma once
#include <iostream>
#include "Food.h"
using namespace std;

//class Food;

class Student
{
public:
    void Learn();
    void Eat(Food food);
public:
    string name = "";
    int num = 0;
    int age = 0;
    Student* deskmate = nullptr;
};
#pragma once
#include <iostream>
#include "Student.h"
using namespace std;

class Class
{
public:
    Class(string name, int capacity);
    //第四个参数 是用来表示 传入的学生是多少的
    Class(string name, int capacity, Student* students[], int count);

public:
    string name = "";
    int capacity = 30;
    Student* students[30] = {nullptr};
};
#include "Food.h"
#include "Person.h"
#include "Student.h"

void Food::BeiEat(Person p) 
{
    cout << name << "被人" << p.name << "吃了" << endl;
}
void Food::BeiEat(Student s) 
{
    cout << name << "被学生" << s.name << "吃了" << endl;
}
#include "Student.h"
#include "Food.h"
void Student::Learn()
{
    cout << name << "开始学习了" << endl;
}

void Student::Eat(Food food)
{
    cout << name << "开始吃" << food.name << ",获得了" << food.kaLuLi << "能量" << endl;
}
// 重载构造函数使用示例
Class c = Class("游戏开发", 30);

写一个Ticket类,有一个距离变量(在构造对象时赋值,不能为负数),有一个价格特征,有一个方法GetPrice可以读取到价格,并且根据距离distance计算价格price(1元/公里)

0~100公里 不打折
101~200公里 大9.5折
201~300公里 打9折
300公里以上 打8折
有一个显示方法,可以显示这张票的信息。
例如:100公里100块钱

#pragma once
class Ticket
{
public:
    //距离
    unsigned distance;
    //价格
    float price;

private:
    //获取价格的方法
    float GetPrice();
public:
    //传入距离的构造函数
    Ticket(unsigned distance);
    //显示票的信息
    void ShowInfo();
};
#include "Ticket.h"
#include <iostream>
using namespace std;

Ticket::Ticket(unsigned distance)
{
    this->distance = distance;
    //通过调用私有方法 计算一次价格
    price = GetPrice();
}

//计算价格
float Ticket::GetPrice()
{
    //300公里以上 打8折
    if (distance > 300)
    {
        return distance * 0.8f;
    }
    //201~300 打9折
    else if (distance <= 300 && distance >= 201)
    {
        return distance * 0.9f;
    }
    //101~200 打9.5折
    else if (distance <= 200 && distance >= 101)
    {
        return distance * 0.95f;
    }
    //0~100 不打折
    else
    {
        return distance;
    }
}

void Ticket::ShowInfo()
{
    cout << distance << "公里" << price << "块钱" << endl;
}
Ticket t = Ticket(100);
t.ShowInfo(); //100公里100块钱

6.4 练习题代码

Person.h

#pragma once
#include <iostream>
#include "Food.h"
using namespace std;

//前向声明
//class Food;

class Person
{
public:
    Person();
    Person(string name, float height);
    Person(string name, float height, int age, string homeAddress);

    void Speak(string str);
    void Walk();
    void Eat(Food food);
    int GetMoney()
    {
        return money;
    }

    void SetMoney(int value)
    {
        money = value;
    }
public:
    string name = "韬";
    float height = 1;
    int age = 1;
    string homeAddress = "广州";

private:
    int money = 999;

};

Person.cpp

#include "Person.h"
#include "Food.h"

Person::Person()
{
    //这里面什么都没写 那具体成员变量的值 就会使用 在声明赋值时采用的值了
}

Person::Person(string name, float height) :name(name), height(height)
{
    //通过列表形式初始化了两个成员变量
}

Person::Person(string name, float height, int age, string homeAddress):Person(name,height)
{
    //委托构造函数初始化了两个 复用了代码
    //自己写了两个参数初始化
    this->age = age;
    this->homeAddress = homeAddress;
}


void Person::Speak(string str)
{
    cout << name << "说:" << str << endl;
}

void Person::Walk()
{
    cout << name << "开始走路了" << endl;
}

void Person::Eat(Food food)
{
    cout << name << "开始吃" << food.name << ",获得了" << food.kaLuLi << "能量" << endl;
}

Student.h

#pragma once
#include <iostream>
#include "Food.h"
using namespace std;

//class Food;

class Student
{
public:
    void Learn();
    void Eat(Food food);
public:
    string name = "";
    int num = 0;
    int age = 0;
    Student* deskmate = nullptr;
};

Student.cpp

#include "Student.h"
#include "Food.h"
void Student::Learn()
{
    cout << name << "开始学习了" << endl;
}

void Student::Eat(Food food)
{
    cout << name << "开始吃" << food.name << ",获得了" << food.kaLuLi << "能量" << endl;
}

Class.h

#pragma once
#include <iostream>
#include "Student.h"
using namespace std;
class Class
{
public:
    Class(string name, int capacity);
    //第四个参数 是用来表示 传入的学生是多少的
    Class(string name, int capacity, Student* students[], int count);
public:
    string name = "";
    int capacity = 30;
    Student* students[30] = {nullptr};
};

Class.cpp

#include "Class.h"

Class::Class(string name, int capacity):name(name),capacity(capacity)
{
    
}

Class::Class(string name, int capacity, Student* students[], int count) : Class(name, capacity)
{
    for (int i = 0; i < count; i++)
    {
        this->students[i] = students[i];
    }
}

Food.h

#pragma once
#include <iostream>
using namespace std;

//前向声明 告诉编译器 有这两个类 具体实现 先不管
class Person;
class Student;

class Food
{
public:
    string name;
    int kaLuLi;

public:
    void BeiEat(Person p);
    void BeiEat(Student s);
};

Food.cpp

#include "Food.h"
#include "Person.h"
#include "Student.h"

void Food::BeiEat(Person p) 
{
    cout << name << "被人" << p.name << "吃了" << endl;
}
void Food::BeiEat(Student s) 
{
    cout << name << "被学生" << s.name << "吃了" << endl;
}

Ticket.h

#pragma once
class Ticket
{
public:
    //距离
    unsigned distance;
    //价格
    float price;

private:
    //获取价格的方法
    float GetPrice();
public:
    //传入距离的构造函数
    Ticket(unsigned distance);
    //显示票的信息
    void ShowInfo();

};

Ticket.cpp

#include "Ticket.h"
#include <iostream>
using namespace std;

Ticket::Ticket(unsigned distance)
{
    this->distance = distance;
    //通过调用私有方法 计算一次价格
    price = GetPrice();
}

//计算价格
float Ticket::GetPrice()
{
    //300公里以上 打8折
    if (distance > 300)
    {
        return distance * 0.8f;
    }
    //201~300 打9折
    else if (distance <= 300 && distance >= 201)
    {
        return distance * 0.9f;
    }
    //101~200 打9.5折
    else if (distance <= 200 && distance >= 101)
    {
        return distance * 0.95f;
    }
    //0~100 不打折
    else
    {
        return distance;
    }
}

void Ticket::ShowInfo()
{
    cout << distance << "公里" << price << "块钱" << endl;
}

Lesson06_练习题.cpp

#include <iostream>
#include "Person.h"
#include "Class.h"
#include "Ticket.h"
using namespace std;
int main()
{
    #pragma region 练习题一
    /*基于成员方法练习题
    对人类的构造函数进行重载,用人类创建若干个对象*/

    Person p;
    cout << p.age << endl;
    cout << p.name << endl;
    cout << p.homeAddress << endl;
    cout << p.height << endl;

    Person p2 = Person("韬老师", 10);
    cout << p2.age << endl;
    cout << p2.name << endl;
    cout << p2.homeAddress << endl;
    cout << p2.height << endl;

    Person p3 = Person("韬老师", 10, 18, "Gay都");
    cout << p3.age << endl;
    cout << p3.name << endl;
    cout << p3.homeAddress << endl;
    cout << p3.height << endl;

    //输出:
    //1
    //韬
    //广州
    //1
    //1
    //韬老师
    //广州
    //10
    //18
    //韬老师
    //Gay都
    //10

    #pragma endregion

    #pragma region 练习题二
    /*基于成员变量练习题
    对班级类的构造函数进行重载,用班级类创建若干个对象*/

    Class c = Class("游戏开发", 30);

    #pragma endregion

    #pragma region 练习题三
    /*写一个Ticket类,有一个距离变量(在构造对象时赋值,不能为负数),有一个价格特征,
    有一个方法GetPrice可以读取到价格,并且根据距离distance计算价格price(1元 / 公里)
    0~100公里 不打折
    101~200公里 大9.5折
    201~300公里 打9折
    300公里以上 打8折
    有一个显示方法,可以显示这张票的信息。
    例如:100公里100块钱*/

    Ticket t = Ticket(100);
    t.ShowInfo();//100公里100块钱

    #pragma endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏