C++面向对象编程之三:初始化列表、类对象作为类成员、静态成员

news2024/10/3 8:28:10

初始化列表

C++提供了初始化列表语法,可以用于成员属性初始化。

语法规则:

无参构造函数():属性1(值1), 属性2(值2), ...
{
}

有参构造函数(形参1, 形参2, ...):属性1(形参1), 属性2(形参2), ...
{
}

example:写一个怪物类,有怪物id和血量属性,并用初始化列表的形式初始化成员属性

#include <iostream>
using namespace std;

class Monster
{
    public:
    Monster():m_monsterId(10001), m_blood(1000)
    {

    }

    Monster(const int monsterId, const int blood):m_monsterId(monsterId), m_blood(blood)
    {

    }

    void print_monster_info()
    {
        cout << "怪物id = " << m_monsterId << ",血量 = " << m_blood << endl;
    }

    private:
    int m_monsterId; //怪物id
    int m_blood; //血量
};

int main(int argc, char *argv[])
{
    Monster m1;
    m1.print_monster_info();

    Monster m2(10002, 2000);
    m2.print_monster_info();

    return 0;
}

类对象作为类成员

C++中的另一个类的对象可以作为类的成员,我们称为该成员为对象成员。那么这时是先构造对象成员,还是先构造该类呢?答案是先构造对象成员后构造该类,析构函数刚好相反,先析构该类后析构对象成员

example:举一个有趣的例子,有一个技能类,一个怪物类,这时怪物会有技能了,所以在设计怪物类时,会有技能类作为怪物类的成员。用来验证另一个类的对象作为类成员时,先构造对象成员,后构造该类。析构则先析构该类,后析构对象成员。

#include <iostream>
using namespace std;

enum SKILL_TYPE
{
    SUB_DEST_BLOOD_ADD_SELF_BLOOD = 0
};

int g_line = 0;

class Monster;

class Skill
{
    public:
    Skill():m_skillType(SUB_DEST_BLOOD_ADD_SELF_BLOOD), m_val(500)
    {
        g_line++;
        cout << g_line << "行:Skill()无参构造函数被调用" << endl;
    }
    Skill(const int skillType, const int val):m_skillType(skillType), m_val(val)
    {
        g_line++;
        cout << g_line << "行:Skill(const int skillType, const int val)有参构造函数被调用" << endl;
    }
    Skill(const Skill &s)
    {
        m_skillType = s.m_skillType;
        m_val = s.m_val;
        
        g_line++;
        cout << g_line << "行:Skill(const Skill &s)拷贝构造函数被调用" << endl;
    }

    ~Skill()
    {
        g_line++;
        cout << g_line << "行:~Skill()析构函数被调用" << endl;
    }

    //施法
    void spell(Monster &src, Monster &dest);

    private:
    int m_skillType; //技能类型
    int m_val;
};

class Monster
{
    public:
    Monster():m_monsterId(10001), m_name("怪物"), m_blood(1000), m_skill()
    {
        g_line++;
        cout << g_line << "行:Monster()无参构造函数被调用" << endl;
    }
    Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal):
    m_monsterId(monsterId), m_name(name), m_blood(blood), m_skill(skillType, skillVal)
    {
        g_line++;
        cout << g_line << "行:Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal)有参构造函数被调用" << endl;
    }
    Monster(const Monster &m)
    {
        m_monsterId = m.m_monsterId;
        m_name = m.m_name;
        m_blood = m.m_blood;
        m_skill = m.m_skill;

        g_line++;
        cout << g_line << "行:Monster(const Monster &m)拷贝构造函数被调用" << endl;
    }

    void setBlood(const int blood)
    {
        m_blood = blood;
    }
    int getBlood()
    {
        return m_blood;
    }

    void setName(const int name)
    {
        m_name = name;
    }

    string getName()
    {
        return m_name;
    }

    //释放技能
    void spell(Monster &dest)
    {
        m_skill.spell(*this, dest);
    }

    ~Monster()
    {
        g_line++;
        cout << g_line << "行:~Monster()析构函数被调用" << endl;
    }

    void print_monster_info()
    {
        g_line++;
        cout << g_line << "行:怪物id = " << m_monsterId << ",名字 = " << m_name << ",血量 = " << m_blood <<  endl;
    }

    private:
    int m_monsterId; //怪物id
    string m_name; //怪物名字
    int m_blood; //血量
    Skill m_skill; //技能
};

//施法
void Skill::spell(Monster &src, Monster &dest)
{
    switch (m_skillType)
    {
        case SUB_DEST_BLOOD_ADD_SELF_BLOOD:
        {
            //dest
            int destBlood = dest.getBlood();
            destBlood -= m_val;
            if (destBlood < 0)
                destBlood = 0;
            dest.setBlood(destBlood);

            //src
            int srcBlood = src.getBlood();
            srcBlood += m_val;
            if (srcBlood < 0)
                srcBlood = 0;
            src.setBlood(srcBlood);

            g_line++;
            cout << g_line << "行:" << src.getName() << " 攻击了 " << dest.getName() << endl;
            g_line++;
            cout << g_line << "行:" << src.getName() << "的血量增加到:" << src.getBlood() << endl;
        g_line++;
        cout << g_line << "行:" << dest.getName() << "的血量减少到 " << dest.getBlood() << endl;
            break;
        }
        default:
        g_line++;
        cout << g_line << "行:技能类型未处理:" << m_skillType << endl;
    }
}

int main(int argc, char *argv[])
{
    Monster m1(10001, "雪女", 10000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 1000);
    Monster m2(10002, "紫衣仙子", 20000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 2000);
    m1.print_monster_info();
    m2.print_monster_info();

    m1.spell(m2);    
    return 0;
}

接下来我们再根据打印结果进行代码分析:

Monster m1(10001, "雪女", 10000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 1000);
另一个类的对象可以作为类的成员时,会先构造对象成员,再构造改该类,析构则刚好相反,先析构
该类,再析构对象成员。所以执行该语句,调用构造函数的顺序为:
(1)Skill类有参构造函数被调用
(2)Monster类有参构造函数被调用

Monster m2(10002, "紫衣仙子", 20000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 2000);
另一个类的对象可以作为类的成员时,会先构造对象成员,再构造改该类,析构则刚好相反,先析构
该类,再析构对象成员。所以执行该语句,调用构造函数的顺序为:
(1)Skill类有参构造函数被调用
(2)Monster类有参构造函数被调用

main函数执行结束时,会调用析构函数释放资源,调用析构函数的顺序为:
(1)m2被析构:Monster类析构函数被调用
(2)m2的m_skill对象成员被析构:Skill类析构函数被调用
(3)m1被析构:Monster类析构函数被调用
(4)m1的m_skill对象成员被析构:Skill类析构函数被调用

静态成员

静态成员包括:

  1. 静态成员变量:就是在成员变量前加上关键词static修饰的成员变量

  1. 所有对象共享一份数据

  1. 可以通过类名进行访问:Monster::ms_counter;

  1. 可以通过对象进行访问:Monster m; m.ms_counter;

  1. 在编译阶段分配内存

  1. 类内声明,类外初始化

  1. 有(public,protected,private)访问权限

  1. 静态成员函数:就是在成员函数前加上关键字static修饰的成员函数

  1. 所有对象共享同一个函数

  1. 可以通过类名进行访问:Monster::getMonsterCounter();

  1. 可以通过对象进行访问:Monster m; m.getMonsterCounter();

  1. 静态成员函数的函数体内只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数

  1. 有(public,protected,private)访问权限

example:测试静态成员变量,所有对象共享一份,类内声明,类外初始化,以及两种访问方式

#include <iostream>
using namespace std;

class Monster
{
    public:
    Monster():m_monsterId(0)
    {
        ms_counter++;
    }
    Monster(const int monsterId):m_monsterId(monsterId)
    {
        ms_counter++;
    }
    Monster(const Monster &m)
    {
        m_monsterId = m.m_monsterId;
        ms_counter++;
    }

    ~Monster()
    {
        ms_counter--;
    }

    static int ms_counter; //怪物个数

    private:
    int m_monsterId; //怪物id
};

int Monster::ms_counter = 0; //静态成员类外初始化

int main(int argc, char *argv[])
{
    //1.静态成员用类名进行访问
    cout << "ms_counter = " << Monster::ms_counter << endl;;

    //2.静态成员用对象进行访问
    Monster m1;
    cout << "ms_counter = " << m1.ms_counter << endl;
    Monster m2;
    cout << "ms_counter = " << m2.ms_counter << endl;
    return 0;
}

需要注意的是,静态成员变量也有(public,protected,private)访问权限的,如果我们将以上代码中的,静态成员变量声明为private权限,类外将不能对静态成员变量进行访问

example:测试静态成员函数所有对象共享一份,静态成员函数的函数体内只能访问静态成员变量或静态成员函数,不能访问非静态成员变量和非静态成员函数。以及两种访问方式

#include <iostream>
using namespace std;

class Monster
{
    public:
    Monster():m_monsterId(0)
    {
        ms_counter++;
    }
    Monster(const int monsterId):m_monsterId(monsterId)
    {
        ms_counter++;
    }
    Monster(const Monster &m)
    {
        m_monsterId = m.m_monsterId;
        ms_counter++;
    }

    ~Monster()
    {
        ms_counter--;
    }

    static int getMonsterCounter()
    {
        return ms_counter;
    }

    static void setMonsterCounter(const int counter)
    {
        ms_counter = counter;
        cout << "ms_counter = " << getMonsterCounter() << endl;
        //m_monsterId = 90001; //错误:静态成员函数只能访问静态成员变量或静态成员函数
    }

    private:
    static int ms_counter; //怪物个数

    int m_monsterId; //怪物id
};

int Monster::ms_counter = 0; //静态成员类外初始化

int main(int argc, char *argv[])
{
    //1.静态成员用类名进行访问
    cout << "ms_counter = " << Monster::getMonsterCounter() << endl;;

    //2.静态成员用对象进行访问
    Monster m1;
    cout << "ms_counter = " << m1.getMonsterCounter() << endl;
    Monster m2;
    cout << "ms_counter = " << m2.getMonsterCounter() << endl;
    return 0;
}

需要注意的是,静态成员函数也有(public,protected,private)访问权限的,如果我们将以上代码中的,静态成员函数声明为private权限,类外将不能对静态成员函数进行访问

好了,关于C++面向对象编程之三:初始化列表、类对象作为类成员、静态成员,先写到这。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/397327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【产品设计】ToB 增删改查显算传

入职培训时技术leader说&#xff1a;“我不需要你们太聪明&#xff0c;做好基础的增删改查就可以了。”看似很简单的活&#xff0c;要做好并不容易。基础的坑在哪里呢&#xff1f; 一、 增&#xff08;新增、创建、导入&#xff09; 1. 明确表字段类型 新增的业务是由不同类型…

Android Studio开发APP

1.下载Android Studio 官网下载:Android Studio for Window ... 百度云下载:android-studio-bundle-141.1903250-windows.exe Android Studio 是谷歌推出的一个Android集成开发工具,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用…

js 时间戳转换,全网最牛业务逻辑解析,逐行解析代码,千万不要错过,里面有些错误场景你可能还不知道

一、注意事项 1、js只识别13位的时间戳 2、10位的是unix时间戳 二、小试牛刀--日期类型转换成时间戳 可先将js字符串转换为date类型&#xff0c;再转换为时间戳类型。 字符串格式为 yyyy-MM-dd HH:mm:ss或者yyyy-MM-dd //把字符串转换成时间格式 let date new Date(2023-11-12…

(二十四)操作系统-吸烟者问题

文章目录一、问题描述二、问题分析1&#xff0e;关系分析2&#xff0e;整理思路3&#xff0e;设置信号量三、实现四、总结一、问题描述 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要…

String类的底层原理和版本演变

1 String类的底层演变&#xff08;1&#xff09; JDK8以及之前版本 &#xff08;2&#xff09;JDK9以及之后版本 javaJDK8的字符串存储在char类型的数组里面&#xff0c;在java中&#xff0c;一个char类型占两个字节。但是很多时候&#xff0c;一个字符只需要一个字节就可存储&…

QGC UDP 建立通讯连接

QGC关于通信连接建立部分的程序比较复杂&#xff0c;但感觉写的很厉害&#xff0c;通过读代码把自己的一点理解记录下来。收集接收空地通信管理空地通信的管理在“应用程序设置->通讯连接”中进行通信连接创建&#xff0c;连接和断开。通讯连接添加完成后点击“连接”按钮&a…

SpringBoot+Spring常用注解总结

1. SpringBootApplication 这里先单独拎出SpringBootApplication 注解说一下&#xff0c;虽然我们一般不会主动去使用它。 SpringBootApplication public class SpringSecurityJwtGuideApplication {public static void main(java.lang.String[] args) {SpringApplication.ru…

携程 x TiDB丨应对全球业务海量数据增长,一栈式 HTAP 实现架构革新

随着新冠病毒疫情的缓解和控制&#xff0c;全球旅游业逐渐开始重新复苏。尤其在一些度假胜地&#xff0c;游客数量已经恢复到疫情前的水平。 携程作为全球领先的一站式旅行平台&#xff0c;旗下拥有携程旅行网、去哪儿网、Skyscanner 等品牌。携程旅行网向超过 9000 万会员提供…

微服务进阶之 SpringCloud Alibaba

文章目录微服务进阶&#x1f353;SpringCloud 有何劣势&#xff1f;&#x1f353;SpringCloud Alibaba 提供了什么&#xff1f;提示&#xff1a;以下是本篇文章正文内容&#xff0c;SpringCloud 系列学习将会持续更新 微服务进阶 &#x1f353;SpringCloud 有何劣势&#xff1…

Spring的概述及Spring6快速入门

Spring6 1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单…

Git图解-远程仓库-Gitee

目录 一、前言 二、远程仓库介绍 三、Gitee&#xff08;码云&#xff09;的使用 3.1 注册与登录 3.2 创建项目 3.3 项目初始化 3.4 添加团队成员 四、转视频版 一、前言 接着上篇&#xff1a; Git图解-常用命令操作-可视化 上篇已将Git可是化操作&#xff0c; 接下来&…

Spring整体架构包含哪些组件?

Spring是一个轻量级java开源框架。Spring是为了解决企业应用开发的复杂性而创建的&#xff0c;它使用基本的JavaBean来完成以前只可能由EJB完成的事情。 Spring的用途不仅限于服务器端的开发&#xff0c;从简单性、可测试性和松耦合的角度而言&#xff0c;任何java应用都可以从…

dolphinschedule使用shell任务结束状态研究

背景&#xff1a;配置的dolphin任务&#xff0c;使用的是shell&#xff0c;shell里包含了spark-submit 如下截图。 dolphin shell 介绍完毕&#xff0c;开始说明现象。 有天有人调整了集群的cdp配置&#xff0c;executor-cores max1 我之前这里写的是2&#xff0c;所以spark任…

如何简单的上手JavaFX17+?解决“缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序”等问题

前言 JavaFX是一个Java的一个ui库&#xff0c;但是自从java8以后&#xff0c;它很悲惨的被从OpenJdk中移除了&#xff0c;因此我们使用它来进行开发时&#xff0c;如果我们使用的是8以后的版本&#xff0c;经常会遇到一些问题。这里针对新手做一个简单的引导&#xff08;悲&am…

JDBC知识点全面总结2:JDBC实战编写CRUD

二.JDBC知识点全面总结1&#xff1a;JDBC实战编写CRUD 1.JDBC重要接口&#xff1f; 2.Driver和DriverMangement的关系&#xff1f; 3.JAVA与数据库连接 4.JAVA中使用statement来执行sql语句时&#xff0c;拼接字符串的sql注入问题&#xff1f; 5.使用preparedstatement进行…

【Microsoft】与 Bing AI 进行 ⌈狂飙⌋

&#x1f38a; 今天是3月8号&#xff0c;❤️农历二月十七&#xff0c;&#x1f495;祝广大女同胞们&#x1f469;女神节快乐&#x1f389;&#xff01;——以创作之名致敬女性开发者文章目录序言Ⅰ、Bing AI初体验Ⅱ、代码生成Ⅲ、生成图像Ⅳ、使用次数Ⅴ、总结序言 ​ 近期&…

数据结构与算法之《顺序表》

目录 1.什么是顺序表 顺序表的优势和缺点 顺序表预备知识 顺序表的代码实现 顺序表头部插入 顺序表的销毁 顺序表的头删 顺序表的尾删 顺序表的尾插 顺序表的任意位置插入 顺序表的查找 顺序表的打印 1.什么是顺序表 这篇文章我们来讲一下基础数据结构的顺序表&…

Input子系统(一)启动篇

代码路径 基于AndroidS&#xff08;12.0&#xff09;代码 system/core/libutils/Threads.cppframeworks/base/services- java/com/android/server/SystemServer.java- core- java/com/android/server/input/InputManagerService.java- jni/com_android_server_input_InputMan…

三步 让你的 vscode 自动编译ts文件

三步让你的 vscode 自动编译ts文件 TypeScript环境安装与如何在vscode实现自动编译ts文件? 文章目录三步让你的 vscode 自动编译ts文件前提条件环境安装自动编译运行监视任务时报错&#xff1f;前提条件 安装 node 环境 环境安装 tsc 作用&#xff1a;负责将ts 代码 转为 浏…

感知器算法

感知器算法原理说明 感知器是一种二分类的线性分类算法&#xff0c;其原理基于神经元的工作原理。感知器将输入数据通过加权求和的方式映射到一个输出&#xff0c;然后根据输出的结果进行分类。 具体来说&#xff0c;给定一个训练集 D(x1,y1),(x2,y2),...,(xn,yn)D{(x_1,y_1)…