42.结构体和函数

42.结构体-结构体和函数


42.1 知识点

结构体作为函数参数

struct Student
{
    int age;
    string name;
    Student* p;
};

当结构体作为参数传递时,可以通过以下三种方式:

  • 按值传递(拷贝副本传递给函数)
  • 指针传递(传递结构体地址)
  • 引用传递(传递原结构体)

对于小型结构体,三种方式都可以使用。对于结构复杂的结构体,建议使用引用或指针传递。

void test(Student s)
{
    s.age = 99;
    s.p->name = "小明";
}

Student s = { 18, "韬老狮", nullptr };
s.p = new Student{ 10, "小红", nullptr };

test(s);

cout << s.age << endl;  // 18 不变 不会受到影响
cout << s.p->name << endl;  // 小明 要变 要受到函数内部修改的影响 因为 p 是指针,存储的是地址

函数返回结构体

结构体和其他变量类型一样,可以作为返回值返回。需要注意以下事项:

  • 不能返回局部变量(在函数内部声明的变量),因为它们的生命周期会随着函数调用的结束而结束。如果在外部继续使用这些局部变量,会出现问题。
  • 除非这个局部变量是在堆上分配的空间,否则返回堆上的内容可以直接返回,因为堆上的内容需要我们自己管理释放。
  • 否则只能返回静态变量、全局变量,或者是外部传入的指针或引用变量。

总结

不管变量类型是什么,只要要作为函数返回值,都必须考虑它的生命周期问题。


42.2 知识点代码

Lesson42_结构体_结构体和函数.cpp

#include <iostream>
using namespace std;

struct Student
{
    int age;
    string name;
    Student* p;
};

void test(Student s);

int main()
{
    std::cout << "结构体和函数\n";

    #pragma region 知识点一 结构体作为函数参数

    Student s = { 18, "韬老狮", nullptr };
    s.p = new Student{ 10, "小红", nullptr };

    test(s);

    cout << s.age << endl;//18 不变 不会受到影响
    cout << s.p->name << endl;//小明 要变 要受到函数内部修改的影响 因为p是指针 存储的是地址 

    #pragma endregion
}

#pragma region 知识点一 结构体作为函数参数

//当结构体作为参数传递时
//可以通过以下三种方式:
//1.按值传递(拷贝副本传递给函数)
//2.指针传递(传递结构体地址)
//3.引用传递(传递原结构体)
//对于小型结构体三种方式随便用
//对于结构复杂的建议使用引用或指针传递
void test(Student s)
{
    s.age = 99;
    s.p->name = "小明";
}

#pragma endregion

#pragma region 知识点二 函数返回结构体

//结构体和其他变量类型一样,可以作为返回值返回
//注意事项也是一致的,要考虑返回对象的生命周期

//函数返回值的时候
//1.不能去返回局部变量(在函数内部声明的变量)生命周期会随着函数调用的结束而结束
//  如果在外部继续去使用这种局部变量 会出现问题
//2.除非你这个局部变量时在堆上去分配的空间,那么可以直接返回,因为堆上内容需要我们自己管理释放
//3.否则就只能返回 静态变量 或者 全局变量 才可以,或者是外部传入的指针或者引用变量才行

//总结,不管变量类型是什么 只要要作为函数返回值,那我们就必须考虑它的生命周期问题

#pragma endregion

42.3 练习题

应用已学过的知识,实现奥特曼打小怪兽

提示:
结构体描述奥特曼与小怪兽
定义一个方法实现奥特曼攻击小怪兽
定义一个方法实现小怪兽攻击奥特曼

分析:
因为在攻击方法中,奥特曼和怪兽互相需要参数,但是假如直接定义会找不到
可以先声明奥特曼结构体,但不实现
然后在怪兽结构体的怪兽攻击奥特曼的方法中传入奥特曼参数,也不要实现,因为此时不知道奥特曼有哪些成员
在实现奥特曼结构体时,使用 Boss::atkOutMan 再实现怪兽攻击奥特曼的方法

// 定义结构体 OutMan,用于表示奥特曼
struct OutMan;

// 定义结构体 Boss,用于表示怪兽
struct Boss
{
    string name;  // 怪兽的名字
    int atk;  // 怪兽的攻击力
    int def;  // 怪兽的防御力
    int hp;  // 怪兽的生命值

    // 构造函数,用于初始化怪兽的属性
    // @param name 怪兽的名字
    // @param atk 怪兽的攻击力
    // @param def 怪兽的防御力
    // @param hp 怪兽的生命值
    Boss(string name, int atk, int def, int hp) : name(name), atk(atk), def(def), hp(hp) {}

    // 怪兽攻击奥特曼的方法,接收一个 OutMan 的引用作为参数
    // 之所以传递引用参数是希望在内部修改奥特曼内容,外部奥特曼对象也被修改
    void atkOutMan(OutMan& outMan);
};

// 定义结构体 OutMan,用于表示奥特曼
struct OutMan
{
    string name;  // 奥特曼的名字
    int atk;  // 奥特曼的攻击力
    int def;  // 奥特曼的防御力
    int hp;  // 奥特曼的生命值

    // 构造函数,用于初始化奥特曼的属性
    // @param name 奥特曼的名字
    // @param atk 奥特曼的攻击力
    // @param def 奥特曼的防御力
    // @param hp 奥特曼的生命值
    OutMan(string name, int atk, int def, int hp) : name(name), atk(atk), def(def), hp(hp) {}

    // 奥特曼攻击怪兽的方法,接收一个 Boss 的引用作为参数
    // 之所以传递引用参数是希望在内部修改怪兽内容,外部怪兽对象也被修改
    void atkBoss(Boss& monster)
    {
        // boss 减血,减去奥特曼攻击力-怪兽防御力
        monster.hp -= (atk - monster.def);
        // 输出攻击信息
        cout << name << "攻击了" << monster.name << ",造成了" << atk - monster.def << "点伤害,"
             << monster.name << "还剩余" << monster.hp << "血量" << endl;
    }
};

// 定义完了 OutMan 再去实现 Boss 的攻击方法
// 在对声明好的方法进行定义
// Boss 攻击奥特曼的方法
// @param outMan 奥特曼的引用
void Boss::atkOutMan(OutMan& outMan)
{
    // 奥特曼减血,减去怪兽攻击力-奥特曼防御力
    outMan.hp -= (atk - outMan.def);
    // 输出攻击信息
    cout << name << "攻击了" << outMan.name << ",造成了" << atk - outMan.def << "点伤害,"
         << outMan.name << "还剩余" << outMan.hp << "血量" << endl;
}
// 声明奥特曼和怪兽对象
// 初始化奥特曼对象,名字为"迪迦奥特曼",攻击力为 10,防御力为 5,生命值为 100
OutMan outMan = OutMan("迪迦奥特曼", 10, 5, 100);
// 初始化怪兽对象,名字为"哥斯拉",攻击力为 8,防御力为 4,生命值为 100
Boss boss = Boss("哥斯拉", 8, 4, 100);

// 开始战斗循环
while (true)
{
    // 奥特曼攻击怪兽
    outMan.atkBoss(boss);
    // 判断怪兽是否死亡
    if (boss.hp <= 0)
    {
        // 输出奥特曼胜利信息
        cout << outMan.name << "取得胜利" << endl;
        break;
    }
    // 怪兽攻击奥特曼
    boss.atkOutMan(outMan);
    // 判断奥特曼是否死亡
    if (outMan.hp <= 0)
    {
        // 输出怪兽胜利信息
        cout << boss.name << "取得胜利" << endl;
        break;
    }
    // 提示用户按任意键继续
    cout << "按任意键继续" << endl;
    _getch();
}

42.4 练习题代码

Lesson42_练习题.cpp

#include <iostream>
#include <conio.h>
using namespace std;

//因为在攻击方法中 奥特曼和怪兽互相需要参数 但是假如直接定义会找不到
//可以先声明奥特曼结构体 但是不实现
//然后在怪兽结构体的怪兽攻击奥特曼的方法中 传入奥特曼参数 也不要实现 因为此时不知道奥特曼有哪些成员
//在实现奥特曼结构体
//使用 Boss::atkOutMan 再实现怪兽攻击奥特曼的方法

// 定义结构体 OutMan,用于表示奥特曼
struct OutMan;

// 定义结构体 Boss,用于表示怪兽
struct Boss
{
    string name;  // 怪兽的名字
    int atk;  // 怪兽的攻击力
    int def;  // 怪兽的防御力
    int hp;  // 怪兽的生命值

    // 构造函数,用于初始化怪兽的属性
    // @param name 怪兽的名字
    // @param atk 怪兽的攻击力
    // @param def 怪兽的防御力
    // @param hp 怪兽的生命值
    Boss(string name, int atk, int def, int hp) : name(name), atk(atk), def(def), hp(hp) {}

    // 怪兽攻击奥特曼的方法,接收一个 OutMan 的引用作为参数
    // 之所以传递引用参数是希望在内部修改奥特曼内容,外部奥特曼对象也被修改
    void atkOutMan(OutMan& outMan);
};

// 定义结构体 OutMan,用于表示奥特曼
struct OutMan
{
    string name;  // 奥特曼的名字
    int atk;  // 奥特曼的攻击力
    int def;  // 奥特曼的防御力
    int hp;  // 奥特曼的生命值

    // 构造函数,用于初始化奥特曼的属性
    // @param name 奥特曼的名字
    // @param atk 奥特曼的攻击力
    // @param def 奥特曼的防御力
    // @param hp 奥特曼的生命值
    OutMan(string name, int atk, int def, int hp) : name(name), atk(atk), def(def), hp(hp) {}

    // 奥特曼攻击怪兽的方法,接收一个 Boss 的引用作为参数
    // 之所以传递引用参数是希望在内部修改怪兽内容,外部怪兽对象也被修改
    void atkBoss(Boss& monster)
    {
        // boss 减血,减去奥特曼攻击力-怪兽防御力
        monster.hp -= (atk - monster.def);
        // 输出攻击信息
        cout << name << "攻击了" << monster.name << ",造成了" << atk - monster.def << "点伤害,"
            << monster.name << "还剩余" << monster.hp << "血量" << endl;
    }
};

// 定义完了 OutMan 再去实现 Boss 的攻击方法
// 在对声明好的方法进行定义
// Boss 攻击奥特曼的方法
// @param outMan 奥特曼的引用
void Boss::atkOutMan(OutMan& outMan)
{
    // 奥特曼减血,减去怪兽攻击力-奥特曼防御力
    outMan.hp -= (atk - outMan.def);
    // 输出攻击信息
    cout << name << "攻击了" << outMan.name << ",造成了" << atk - outMan.def << "点伤害,"
        << outMan.name << "还剩余" << outMan.hp << "血量" << endl;
}

int main()
{
    std::cout << "结构体和函数\n";

    // 声明奥特曼和怪兽对象
    // 初始化奥特曼对象,名字为"迪迦奥特曼",攻击力为 10,防御力为 5,生命值为 100
    OutMan outMan = OutMan("迪迦奥特曼", 10, 5, 100);
    // 初始化怪兽对象,名字为"哥斯拉",攻击力为 8,防御力为 4,生命值为 100
    Boss boss = Boss("哥斯拉", 8, 4, 100);

    // 开始战斗循环
    while (true)
    {
        // 奥特曼攻击怪兽
        outMan.atkBoss(boss);
        // 判断怪兽是否死亡
        if (boss.hp <= 0)
        {
            // 输出奥特曼胜利信息
            cout << outMan.name << "取得胜利" << endl;
            break;
        }
        // 怪兽攻击奥特曼
        boss.atkOutMan(outMan);
        // 判断奥特曼是否死亡
        if (outMan.hp <= 0)
        {
            // 输出怪兽胜利信息
            cout << boss.name << "取得胜利" << endl;
            break;
        }
        // 提示用户按任意键继续
        cout << "按任意键继续" << endl;
        _getch();
    }
}

#pragma region 练习题

// 应用已学过的知识,实现奥特曼打小怪兽
// 提示:
// 结构体描述奥特曼与小怪兽
// 定义一个方法实现奥特曼攻击小怪兽
// 定义一个方法实现小怪兽攻击奥特曼

#pragma endregion


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

×

喜欢就点赞,疼爱就打赏