【C++技能树】类和对象的使用 --初始化列表,static,友元,内部类,匿名对象的理解与使用

news2024/11/19 16:38:55

在这里插入图片描述
Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我bua!

类和对象的使用

  • 0. 初始化列表
    • explicit关键字
  • 1.Static静态成员变量
  • 2.友元
    • 2.1.友元函数
    • 2.2.友元类
  • 3.内部类
  • 4.匿名对象
  • 4.匿名对象
  • 至此初始化列表,static,友元,内部类,匿名对象的理解与使用结束

在这里插入图片描述

0. 初始化列表

这是一个C++的默认构造函数

class Date{
public:
    Date(int year,int month,int day)
    {
        _year=year;
        _month=month;
        _day=day;
    }
private:
    int _year;
    int _month;
    int _day;
};

虽然我们大多时候混淆初始化与赋值的概念,但在这里,构造函数体中只能成为赋值,因为初始化只能初始化一次,而赋值可以赋值多次。那么在哪里进行初始化呢?可能会说在定义时直接初始化,这在日期类中是可以的,但在这种情况当中,显然是不可以的了

class A{
private:
    int __a;
};
class B{
private:
    int &_ref;
    const int _ci;
};

根据前面所学,引用&与限制const必须在定义时就完成初始化。那么我这里该如何初始化呢?我并没有确定我的要使用的对象是什么。

这就是初始化列表存在的意义了

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或者表达式

class B{
public:
    B(int& ref,int ci):
        _ref(ref),
        _ci(ci)
    {

    }
private:
    int &_ref;
    const int _ci;
};

所以,这样解决了引用&与限定const的初始化问题:可以根据具体场景进行初始化了。

注意:

  1. 每个成员变量仅能在初始化列表中出现一次!因为初始化只能初始化一次

  2. 类中包含 引用、const成员变量及自定义类型成员(且该类没有默认构造函数时),必须将其放在初始化列表中进行初始化!

    class A{
    public:
        A(int a):
            __a(a)
        {
        }
    private:
        int __a;
    };
    class B{
    public:
        B(int a,int& ref,int ci):
            _a(a),
            _ref(ref),
            _ci(ci)
        {
    
        }
    private:
        A _a;
        const int _ci;
        int &_ref;
    };
    

    例如在这个例子中,B类的属性有三个,分别对应了自定义类型成员,引用,const的三种情况。所以需要将其放在初始化列表中进行初始化。

    其初始化顺序为,从成员属性去找对应的初始化列表。

在这里插入图片描述

这是一个初始化列表的特性,当遇到以下这种情况的时候就需要充分考虑到这种特性。

class bug
{
public:
    bug(int a):
    _a1(a),
    _a2(_a1)
    {

    }
private:
    int _a2;
    int _a1;
}

当传入的a=1时_a2与_a1的结果是什么呢?答案是a2为乱码,a1为1,这就是因为初始化列表的初始化顺序是根据声明顺序来进行的,所以导致了这个问题。

在日常中建议多用初始化列表,因为即使在声明时给上初始值,之后也会调用初始化列表进行初始化。

那初始化列表这么有用,是不是在函数体内进行声明就没有使用场景了?

class test2
{
public:
    test2():
    a((int *)malloc(sizeof(int)*10))
    {
        if(a==NULL)
        {
            perror("malloc,failed");
        }
    }
private:
    int *a;
};
int main()
{
   test2 a;
    return 0;
}

在这段代码中,虽然初始化列表可以完成对a的空间分配,但malloc可能存在分配失败的问题。所以需要对其分配完的空间进行一个检查,此时初始化列表就不能做这件事了。

所以,初始化列表只适用于初始化变量,这一件事,其他事仍然需要放在函数体内执行

explicit关键字

class trans{
public:
    trans(int a=0)
    {
        _a=a;
        cout<<"trans()";
    }
    trans(const trans& T)
    :_a(T._a)
    {
        cout<<"trans(const trans&T)";
    }
    void print()
    {
        cout<<_a<<endl;
    }
private:
    int _a;
};
int main()
{
    trans T1(1);
    trans T2=2;
    const trans& T3=2;
    return 0;
}

T1与T2的初始化非常的常见,前面也介绍了很多类似的用法,但需注意,T2此时有一个隐式类型转换
在这里插入图片描述

会先将内置类型调用一次构造函数,将其变为trans对象,之后再调用一次初始化构造,完成对T2的初始化。(但经过编译器优化后这种行为只会出现一次)

所以T3为什么需要加上const?因为在完成转换后,临时构造出的trans会被销毁,而&又不能引用一个销毁的对象,所以需要const延长其生命周期。

当然,我们如果不想发生这种隐式类型转换的行为,可以在构造函数前加入 explicit的关键字。

class trans{
public:
    explicit trans(int a=0)
    {
        _a=a;
        cout<<"trans()";
    }
    trans(const trans& T)
    :_a(T._a)
    {
        cout<<"trans(const trans&T)";
    }
    void print()
    {
        cout<<_a<<endl;
    }
private:
    int _a;
};
int main()
{
    trans T1(1);
    trans T2=2;
    return 0;
}

1.Static静态成员变量

看看以下这个程序,创建了多少类对象。

class sta{
public:
    sta(){
        _count++;
    }
    sta(const sta&st)
    {
        _count++;
    }
    ~sta()
    {
        --_count;
    }
    static int _count;
};
int sta::_count=0;
sta aa0;

void Func()
{
	static sta aa2;
	cout << __LINE__ << ":" << sta::_count << endl;

	sta::_count++;
}

int main()
{
	cout <<__LINE__<<":"<< sta::_count << endl;  // 1
	sta aa1;
	
	Func();  // 3
	Func();  // 3

	return 0;
}

从中可以看出static对象的作用:对于全局变量进行了进一步的封装

  1. 静态成员为所有类对象所共享,不属于某个具体的对象
  2. 静态成员变量在类内声明,在类外定义,定义时不加static关键字
  3. 可以通过类名::变量名 或者 对象.静态成员来访问
  4. 静态成员函数没有this指针,所以不能访问任何非静态成员
  5. 静态成员也是类的成员,受访问限定符限制

一道用到了static变量特性的oj题
在这里插入图片描述

class sum
{
public:
    sum()
    {
        n++;
        s+=n;
    }
    static int s;
    static int n;
};
int sum:: s=0;
int sum:: n=0;
class Solution {
public:
    int Sum_Solution(int n) {
        sum s1[n];
        return sum::s;
    }
};

2.友元

2.1.友元函数

友元函数可以直接访问类的私有成员,使用时在类中的任意位置声明即可,

class test0
{
public:
private:
    int score=100;
};
void print(const test0& t0)
{
    cout<<t0.score<<endl;
}

在这里,是无法访问score这个变量的,因为score为私有成员属性,所以无法在类外进行访问.但加入友元函数的声明后可以突破这个限制

class test0
{
friend void print(const test0&t0);
public:
private:
    int score=100;
};
void print(const test0& t0)
{
    cout<<t0.score<<endl;
}
  1. 友元函数可以访问类的私有成员和保护成员,但其不是类的成员函数
  2. 友元函数因为不是类的成员函数,没有this指针,所以不可以且没必要用后置const进行修饰
  3. 友元函数可以在类定义的任何地方进行声明
  4. 一个函数可以与多个类达成友元关系
  5. 调用原理与普通函数相同

2.2.友元类

在一个类中声明加入另一个类的友元声明,在另一个类中即可访问这个类受保护的成员变量.

class test0
{
friend void print(const test0&t0);
friend class test3;
public:
private:
    int score=100;
};
class test3
{
public:
    void print()
    {
        cout<<t0.score;
    }
private:
    test0 t0;
};
  1. 友元关系是单向的,test3可以访问test0的成员变量,反之却不可以
  2. 友元关系不能传递

3.内部类

一个类定义定义在一个类的里面.

class A
{
public:
    class B{
    private:
        int _b;
    };
private:
    static int _a1;
    int _a2;
};
int A:: _a1=0;
int main()
{
	return 0;
}

计算A的大小时,仅计算A中的成员,例如这里sizeof(A)=4.

内部类是外部类的友元,访问静态变量的时候不需要加类作用域修饰符

这里B是A的友元,但A不是B的友元

class A
{
public:
    class B{
    public:
        void print(const A&a)
        {
            cout<<a._a2<<_a1;
        }
    private:
        int _b;
    };
private:
    static int _a1;
    int _a2;
};

4.匿名对象

当想使用一个类中的某一个函数时可以直接使用匿名对象.

class C{
public:
    void print()
    {
        cout<<6;
    }
};
int main()
{
    C a; 
    a.print();
	C().print();
}

正常的调用需要像main中前两行写一样,但加入了匿名函数的规则之后,直接按照第三个规则来写即可.匿名函数的生命周期仅在当前行.

void print(const A&a)
        {
            cout<<a._a2<<_a1;
        }
    private:
        int _b;
    };
private:
    static int _a1;
    int _a2;
};

4.匿名对象

当想使用一个类中的某一个函数时可以直接使用匿名对象.

class C{
public:
    void print()
    {
        cout<<6;
    }
};
int main()
{
    C a; 
    a.print();
	C().print();
}

正常的调用需要像main中前两行写一样,但加入了匿名函数的规则之后,直接按照第三个规则来写即可.匿名函数的生命周期仅在当前行.

至此初始化列表,static,友元,内部类,匿名对象的理解与使用结束

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

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

相关文章

【Linux】2. Shell运行原理与Linux权限操作

专栏导读 &#x1f341;作者简介&#xff1a;余悸&#xff0c;在读本科生一枚&#xff0c;致力于 C方向学习。 &#x1f341;收录于 C 专栏&#xff0c;本专栏主要内容为 C 初阶、 C 进阶、 STL 详解等&#xff0c;持续更新中&#xff01; &#x1f341;相关专栏推荐&#xff1…

Cloud Studio 有“新”分享

GitHub仓库推荐 Awesome Open Source Applications - 收集了各种开源应用程序&#xff0c;包括 Web 应用、桌面应用、移动应用等。Cloud Studio 一键运行 Free for Dev - 收集了各种免费的开源应用程序和工具&#xff0c;包括 Web 应用、桌面应用、移动应用等。Cloud Studio 一…

kaggle经典赛 | IEEE欺诈检测竞赛金牌方案分享

https://www.kaggle.com/competitions/ieee-fraud-detection 赛题背景 想象一下&#xff0c;站在杂货店的收银台&#xff0c;身后排着长队&#xff0c;收银员不那么安静地宣布你的卡被拒绝了。在这一刻&#xff0c;你可能没有考虑决定你命运的数据科学。 尴尬&#xff0c;并…

一文搞定验证码(上部分)

1.背景 目前收到反馈,存在一类用户,在利用会员权益大量进行二次销售;而且还是自动进行操作的. 那么意味着他们有一个自动平台在对我们的商品进行二次销售. 这是就该我们的主角登场了. 验证码模块可以有效防止机器人刷接口 2.开源验证码框架 通过在网上查找资料, 发现了几个验…

C++:采用哈希表封装unordered_map和unordered_set

目录 一. 如何使用一张哈希表封装unordered_map和unordered_set 二. 哈希表迭代器的实现 2.1 迭代器成员变量及应当实现的功能 2.2 operator函数 2.3 operator*和operator->函数 2.4 operator!和operator函数 2.5 begin()和end() 2.6哈希表迭代器实现代码 三. unord…

渗透测试--6.2.mdk3攻击wifi

前言 本次依然使用Kali虚拟机系统&#xff0c;win11主机&#xff0c;网卡Ralink 802.11 配合mdk3进行wifi伪造、连接设备查看、解除认证攻击。本次实验只用于学习交流&#xff0c;攻击目标为自家的手机热点&#xff0c;请勿违法使用&#xff01; 目录 前言 1.Deauth攻击原…

Electron简介、安装、实践

本文中的所有代码均存放在https://github.com/MADMAX110/my-electron-app Electron是什么&#xff1f; Electron是一个开源的框架&#xff0c;可以使用JavaScript, HTML和CSS来构建跨平台的桌面应用程序。Electron的核心是由Chromium和Node.js组成&#xff0c;它们分别提供了渲…

【springboot 开发工具】接口文档我正在使用它生成,舒坦

前言 先来描述下背景&#xff1a;由于新公司业务属于自研产品开发&#xff0c;但是发现各产品业务线对于接口文档暂时还是通过集成Swagger来维护&#xff0c;准确来说是knife4j&#xff08;Swagger的增强解决方案&#xff09;。但是对于5年的后端开发老说&#xff0c;早就厌倦…

Java-线程安全的四个经典案例和线程池

单例模式 有些对象&#xff0c;在一个程序中应该只有唯一 一个实例&#xff08;光靠人保证不靠谱 借助语法来保证&#xff09; 就可以使用单例模式 在单例模式下 对象的实例化被限制了 只能创建一个 多了的也创建不了 单例模式分为两种&#xff1a;饿汉模式和懒汉模式 饿汉模式…

[Java基础]—SpringBoot

Springboot入门 Helloworld 依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version> </parent><dependencies><depend…

软件测试基础知识整理(四)- 软件开发模型、测试过程模型

目录 一、软件开发模型 1.1 瀑布模型 1.1.1 特点 1.1.2 优缺点 1.2 快速原型模型&#xff08;了解&#xff09; 1.2.1 特点 1.2.2 优缺点 1.3 螺旋模型&#xff08;了解&#xff09; 1.3.1 特点 1.3.2 优缺点 二、测试过程模型 2.1 V模型&#xff08;重点&#xff…

LeetCode_29. 两数相除

目录 题目描述 思路分析 我的题解 题目描述 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff…

8个免费的高质量UI图标大全网站

UI图标素材是设计师必不可少的设计元素。 高质量的UI图标会让设计师的设计效率事半功倍。 本文分享8个免费的高质量UI图标大全网站。 即时设计资源社区 即时设计资源广场中精选了多款专业免费的UI图标设计资源&#xff0c;无需下载即可一键保存源文件&#xff0c;同时还提供…

深入浅析Linux Perf 性能分析工具及火焰图

Perf Event 子系统 Perf 是内置于 Linux 内核源码树中的性能剖析&#xff08;profiling&#xff09;工具。它基于事件采样的原理&#xff0c;以性能事件为基础&#xff0c;支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位…

Maven PKIX path building failed 错误提示

最近公司的项目突然出现了下面的提示。 PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -> [Help 2]问题和解决 出现上面的提示的问题是因为 SSL 签名的问题。 …

经典面试题:理解Cookie和Session之间的区别

文章目录 一、Cookie概念先知1、Cookie是什么&#xff1f;2、Cookie从哪里来&#xff1f;3、Cookie要存到哪里去&#xff1f;4、Cookie是存在哪里的&#xff1f;5、浏览器是如何通过Cookie来记录的&#xff1f;6、Cookie的过期时间有什么用&#xff1f; 二、见见Cookie三、会话…

软件设计师考试笔记,已通过

目录 系统可靠度 外部实体 内聚类型 编译过程 逆波兰式 前驱图 scrum框架模型 编译和解释 有限自动机 聚簇索引和非聚簇索引 二叉树的前序,中序,后序遍历 动态规划贪心算法 算法 01背包问题 系统可靠度 1. 串联部件可靠度 串联部件想要这条路走通&#xff0c;只有…

软件测试行业7年了,薪资从10k到了22k,感觉到头了?

蜕变之前 明天的希望&#xff0c;让我们忘了今天的痛苦。 怎样区别一个废柴和一个精英&#xff1f;看外貌&#xff0c;看气质&#xff0c;看谈吐&#xff0c;看消费… 有人忙着把人和人进行分类&#xff0c;有人忙着怎么从这一阶层过渡到上一阶层。当你很累的时候&#xff0c…

引入外部文件实现步骤

1.引入数据库相关依赖 2.创建外部属性文件&#xff0c;properties格式&#xff0c;定义数据信息&#xff1a;用户名 密码 地址等 3.创建spring配置文件&#xff0c;引入context命名空间&#xff0c;引入属性文件&#xff0c;使用表达式完成注入 <beans xmlns"http://w…

交友项目【集成环信Api】

目录 1&#xff1a;自动装配 2&#xff1a;查询用户环信账户 3&#xff1a;环信ID查询用户信息 1&#xff1a;自动装配 在项目中集成环信API&#xff0c;完成即时通信等 环信官方文档地址&#xff1a;Java Server SDK [IM 开发文档] 自动装配模块&#xff1a; pom文件相关…