类和对象(下+)_const成员、初始化列表、友元、匿名对象

2024-06-09 07:20

本文主要是介绍类和对象(下+)_const成员、初始化列表、友元、匿名对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

类和对象(下+)


文章目录

  • 类和对象(下+)
  • 前言
  • 一、const成员
  • 二、友元
    • 1.友元函数
    • 2.友元类
  • 三、初始化列表
  • 四、explicit关键字
  • 五、匿名对象
  • 总结


前言

static成员、内部类、const成员、初始化列表、友元、匿名对象


一、const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

其特性如下:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print()//Date* this{cout << _year << "年" << _month << "月" << _day << "日" << endl;}void Print()const //const Date* this{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1(2028, 1, 11);d1.Print();const Date d2(2028, 8, 18);d2.Print();return 0;
}

匹配原则(找最合适的)【权限不能放大】:
d1调用第一个Print(带const修饰)
d2调用第二个Print(不带const修饰
在这里插入图片描述
认为是2钟不同的类型,构成函数重载
但是在这里这样使用const是没有意义的。


写一个简单的顺序表

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cassert>
using namespace std;class SeqList
{
public:void PushBack(int x){_a[_size++] = x;}size_t size()const{return _size;}int operator[](size_t i){assert(i < _size);return _a[i];}
private:int* _a = (int*)malloc(sizeof(int) * 10);size_t _size = 0;size_t _capacity = 0;
};int main()
{SeqList sl;sl.PushBack(1);sl.PushBack(2);sl.PushBack(3);for (size_t i = 0; i < sl.size(); i++){cout << sl[i]<<" ";//cout << sl.operator[](i) << " ";//与上式等价}return 0;
}

在这里插入图片描述
在这里插入图片描述
但是当我们需要修改sl里的内容时是不可以的,原因是重载operator[ ]返回的是int类型的_a[i]的拷贝,具有常性。
这里需要提一下:在这里插入图片描述
在这里插入图片描述
此时就可以进行修改了。


此时如果又需要一个专门用于打印的函数Print,并且在传参时防止sl对象被修改因此加以const修饰,但是此时又会出现新的报错,如下

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cassert>
using namespace std;class SeqList
{
public:void PushBack(int x){_a[_size++] = x;}size_t size(){return _size;}int& operator[](size_t i){assert(i < _size);return _a[i];}
private:int* _a = (int*)malloc(sizeof(int) * 10);size_t _size = 0;size_t _capacity = 0;
};void Print(const SeqList& sl)
{for (size_t i = 0; i < s.size(); i++){cout << sl[i] << " ";}
}int main()
{SeqList sl;sl.PushBack(1);sl.PushBack(2);sl.PushBack(3);Print(sl);return 0;
}

在这里插入图片描述

报错原因出在:const对象调用非const对象,存在权限放大的问题
解决方法:

	size_t size() const{return _size;}//只读int& operator[](size_t i) const{assert(i < _size);return _a[i];}//读 or 写都可以  		//与上一个代码块构成函数重载int& operator[](size_t i){assert(i < _size);return _a[i];}

只需要在size()和[ ]重载函数中加以const修饰this指针,即可。

同样看下面这段日期类

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}bool operator<(const Date& d)const{//this->Print();//this 是非const 的,可以调用const 的Printif (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}return false;}
private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{const Date d1(2028, 1, 11);Date d2(2028, 8, 18);cout << (d1 < d2) << endl;cout << (d1.operator<(d2)) << endl;//与上行代码等价return 0;
}

const Date d1(2028, 1, 11);当d1加上const时,如果operator<不加const的话,会出现错误,原因依旧是d1的类型是const Date*类型,(属于权限放大)


二、友元

1.友元函数

友元函数的几点特性:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数(没有this指针)
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用和普通函数的调用原理相同

还是之前说的日期类,此时我们想要重载一下"<<"如下

	void operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;}

但是当我们想要使用重载的"<<“时,会出现以下情况:
在这里插入图片描述
在这里插入图片描述
因此为了能够正常使用,我们在全局定义关于”<<"的重载函数,但在全局定义又会出现无法访问成员变量的问题,因此此时就需要在Date中进行友元声明,这样在全局定义的函数就可以访问类成员变量了

friend void operator<<(ostream& out, const Date& d);

接下来为了满足cout<<d1<<d2;需要以上返回out,即:

	ostream& operator<<(ostream& out, const Date& d){out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;}

在这里插入图片描述

2.友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员

  1. 友元关系是单向的,不具有交换性
  2. 友元关系不能传递
  3. 友元关系不能继承
class Time
{friend class Date1;
public:friend class Date;	// 声明日期类为时间类的友元类,//则在日期类中就直接访问Time类中的私有成员变量Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour; int _minute; int _second;
};class Date1
{
public:Date1(int year, int month, int day){_year = year;_month = month;_day = day;}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year; // 年int _month; // 月int _day; // 日Time _t;
};

三、初始化列表

就像这样

public://初始化列表,是每个成员定义的地方Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private://每个成员的声明int _year;int _month;int _day;

需要注意的几点是:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关
    在这里插入图片描述
    在构造函数的初始化列表阶段,对内置类型用随机值进行初始化,对自定义类型会调用它的默认构造
    在这里插入图片描述

当_day给了缺省值 int _day=1;
在这里插入图片描述
_day给了缺省值,把它初始化成1 了;接下来还要走函数体变为18,由此可知()缺省值就是给初始化列表用的!!

但是初始化列表也有不能解决初始化问题
(比如要求数组_array初始化一下(在函数体中中memset初始化))

四、explicit关键字

class A
{
public:A(int i):_a(i){cout << "A" << endl;}
private:int _a;
};int main()
{A aa1(1);A aa2 = 2;
}

在这里插入图片描述
因为

  • 单参数构造函数的隐式类型转换
  • 用2调用A构造函数生成一个临时对象,再用这个对象去拷贝构造aa2
  • 编译器会再优化,优化用2直接构造
    在这里插入图片描述
    >A& ref = 2;
    存在类型转换,会生成临时对象,2具有常量性,ref不能引用临时对象,也就是存在权限放大问题,加上const就可以了即:

const A& ref = 3;

紧接着还有一个问题,那么为什么这里ret可以引用2,因为因为这个单参数的函数支持隐式类型转换,单参数函数这个参数(2)的的整型值能转换成一个A的对象,就可以引用了。

如果说bu想让隐式类型转换发生,可以加关键字explicit,加在构造函数的位置

	explicit  A(int i):_a(i){cout << "A" << endl;}

此时在这里插入图片描述
在这里插入图片描述

以上是讨论的单参数的,那么如果是多参数的呢?

c++11支持多参数的转换

class B
{
public:B(int b1,int b2):_b1(b1),_b2(b2){cout << "B" << endl;}private:int _b1;int _b2;
};
int main()
{B bb1(1, 1);B bb2 = {2,2};const B& ref = { 3,3 };//同样的,不加const就不支持引用return 0;
}

道理同上,如果不想让隐式类型转换发生,使用关键字explicit

五、匿名对象

首先来比较一下有名对象和匿名对象:

  • 有名对象 特点:生命周期在当前局部域

A aa4(4);

  • 匿名对象 特点:生命周期只在这一行

A (5);

匿名对象可以用来传参,不用先创建变量,再传参

class A
{
public:explicit  A(int i):_a(i){cout << "A" << endl;}
private:int _a;
};class SeqList
{
public:void PushBack(A x){_a[_size++] = x;}size_t size() const{return _size;}A& operator[](size_t i) const{assert(i < _size);return _a[i];}private:A* _a = (A*)malloc(sizeof(A) * 10);size_t _size = 0;size_t _capacity = 0;
};int main()
{SeqList s;A aa3(3);s.PushBack(aa3);s.PushBack(A(4));//利用匿名对象,直接传参return 0;
}

当在一些特定场景下,适当使用匿名对象可以起到简化代码的作用。

总结

😁 ;

这篇关于类和对象(下+)_const成员、初始化列表、友元、匿名对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1044527

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

API-环境对象

学习目标: 掌握环境对象 学习内容: 环境对象作用 环境对象: 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境。 作用: 弄清楚this的指向,可以让我们代码更简洁。 函数的调用方式不同,this指代的对象也不同。【谁调用,this就是谁】是判断this指向的粗略规则。直接调用函数,其实相当于是window.函数,所以this指代window。

Python分解多重列表对象,isinstance实现

“”“待打印的字符串列表:['ft','bt',['ad',['bm','dz','rc'],'mzd']]分析可知,该列表内既有字符对象,又有列表对象(Python允许列表对象不一致)现将所有字符依次打印并组成新的列表”“”a=['ft','bt',['ad',['bm','dz','rc'],'mzd']]x=[]def func(y):for i in y:if isinst

OC 中的*前const与*后const

int const *p2; int *const p3; 这个什么不能改由const后面的内容决定,如果是*p的话,则*p不可以改。 也就是说p指向的内容不能改变 如果const后面是p的话,则p不能改,也就是说p的指向不可以改变 OC中的字符串的话 NSString* name=@"wangning"; 此时我们不想让外界改变name的值得话const应该加在 NS

OC和 C语言中的const

const与宏对比 1.都是在其他的地方不可以改变 2.一个地方改了其他的地方都会改变。 而且宏定义的缺陷是, 是它会不断的开辟临时变量的存储空间 使用const的话 是都去使用同一的一份空间,使用同一个对象。 加const 之后变量还是全局的,只不过变为全局常量。 如果此时改变量不想被被类外面访问的话,可以加上static关键字, 3.下次想要定义一些宏的时候分

CSS列表属性:list-style系列属性详解

CSS(层叠样式表)是用于控制网页样式的一种语言,它允许开发者以一种非常灵活的方式来设置网页元素的外观。在CSS中,list-style属性族是专门用来设置列表样式的。列表是网页设计中常见的元素,它们可以是有序列表(<ol>)或无序列表(<ul>)。list-style系列属性允许你自定义列表项前的标记,包括类型、位置和图像。 1. list-style-type list-style-typ

Java面试题:内存管理、类加载机制、对象生命周期及性能优化

1. 说一下 JVM 的主要组成部分及其作用? JVM包含两个子系统和两个组件:Class loader(类装载)、Execution engine(执行引擎)、Runtime data area(运行时数据区)、Native Interface(本地接口)。 Class loader(类装载):根据给定的全限定名类名(如:java.lang.Object)装载class文件到Runtim

Class 对象在执行引擎中的初始化过程

一个 class 文件被加载到内存中需要经过 3 大步:装载、链接、初始化。 装载 装载是指 Java 虚拟机查找 .class 文件并生成字节流,然后根据字节流创建 java.lang.Class 对象的过程。 链接 链接过程分为 3 步:验证、准备、解析。 验证: 初始化 这是 class 加载的最后一步,这一阶段是执行类构造器方法的过程,并真正初始化类变量。 1.文件格式检验:检

Android匿名共享内存

ashmem的使用流程如下: 1.ashmem_create_region创建匿名共享内存区域,本质是调用open系统调用 2.ioctl设置共享内存的名字和大小,设置的名字为/dev/ashmem/,名字的存在就为了能够让其他人找到目标 3.mmap映射文件中的虚拟内存以及物理内存 4.直接对着这一块地址区域读写。 其中ioctl必须设置名字和大小,不然没办法进行映射,因为在映射之前进行了校验