C++:类的static成员,友元和构造函数初始化列表

news2024/11/28 19:34:36

目录

一.类的构造函数的初始化列表

1.类的构造函数初始化列表的引入和介绍

2.初始化列表用于类的类对象成员的拷贝构造函数的调用

3.初始化列表的使用细则

4.使用初始化列表的一个注意事项 

二.explicit关键字

三.C++类的static成员

1.类中static修饰的成员变量

2.类中static修饰的成员函数

3.相关练习

四.类的友元函数和友元类

1.类的友元函数

2.类的友元类

五.拷贝对象时的一些编译器优化


章节架构:

 

一.类的构造函数的初始化列表

1.类的构造函数初始化列表的引入和介绍

  • 当一个类对象被创建时,其成员变量的定义(成员变量内存空间的开辟和初始化)是在构造函数函数体被执行之前完成的:

比如:

class Date
{
public:
	Date(int year = 0,int day=0)     Date的构造函数
	{
		cout << "pause" << endl;

		_year = year;
		_day = day;
	}

private:
	int _year;
	int _day;
};


int main()
{
	Date a;
	return 0;
}

  • 因此构造函数体中的语句只是为成员变量赋值,而不是成员变量的初始化(变量的初始化指的是为变量申请内存空间后立马为其赋初值)(初始化在变量的生命周期中只能进行一次,而构造函数体内可以多次赋值)
  • 如果类中存在const成员变量(只可读,不可写入的变量),构造函数是无法为其初始化的。

比如:


 

代码段无法通过编译。

C++为了解决这个问题,设计了构造函数的初始化列表,用来进行成员变量的初始化(为成员变量开辟内存空间后立马赋予其初值)。

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

比如:

class Date
{
public:
	Date(int year = 0, int month = 0, int day = 0)
		: _year(year)                   成员变量的初始化列表
		, _month(month)
		, _day(day)
		, _Cday(3)                      const成员变量的初始化
	{                                   构造函数函数体
		cout << "pause" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	const int _Cday;
};


int main()
{
	Date a;
	return 0;
}

 

2.初始化列表用于类的类对象成员的拷贝构造函数的调用

关于类的拷贝构造函数:http://t.csdn.cn/HNeit

有些编译器(比如vs系列)不会为类的自定义拷贝构造函数添加调用其类对象成员变量的拷贝构造函数的指令,比如:
#include <iostream>

using std::cout;
using std::cin;
using std::endl;

class subdate
{
public:
	subdate()								类的构造函数
		:_a(0)
	{
		cout << "subconstructor" << endl;
	}
	subdate(subdate& date)                  类的拷贝构造函数
	{
		cout << "copysubdate" << endl;
	}

private:
	int _a;
};

class Date
{
public:

	Date(int year = 0, int month = 0, int day = 0)   类的构造函数
		: _year(year)
		, _month(month)
		, _day(day)
		, _Cday(3)
		,_test()
	{
		cout << "constructor" << endl;
	} 
	Date(Date & date)								 类的自定义拷贝构造函数
		: _Cday(3)
	{
		cout << "Dateconstructor" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	const int _Cday;
	subdate _test;

};


int main()
{
	Date a;

	Date b(a);               对象b的创建会调用拷贝Date的自定义拷贝构造函数
	return 0;
}

图解分析:

若想让类的自定义拷贝构造函数可以去调用该类的类对象成员的拷贝构造函数,我们就必须在拷贝构造函数的初始化列表中显式地表明类对象成员的拷贝构造函数的调用

即对Date类的自定义拷贝构造函数作如下修改:

	Date(Date & date)								 //类的自定义拷贝构造函数
		: _Cday(3)
		,_test(date._test)
	{
		cout << "Dateconstructor" << endl;
	}

3.初始化列表的使用细则

  1.  每个成员变量在初始化列表中只能出现一次(因为初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化
  • 引用成员变量
  • const成员变量
  • 类对象成员变量(尤其是该类没有可以无参调用的构造函数时)

比如:

class A
{
public:
    A(int a)         该构造函数不可无参调用
    :_a(a)
    {}
private:
    int _a;
};


class B
{
public:
    B(int a, int ref)   必须将其成员置于初始化列表中
    :_aobj(a)  
    ,_ref(ref)
    ,_n(10)
    {}
private:
    A _aobj;          没有可以无参调用的构造函数
    int& _ref;        引用成员
    const int _n;     const成员变量
};

初始化列表可以让系统在为类成员变量开辟内存空间的同时为成员变量赋初值,并且还可以让我们显式地在自定义构造函数中调用类中类对象成员的构造函数

因此,无论何种情况下,我们都应该尽量在类的构造函数中使用初始化列表初始化成员变量

(成员变量的初始化顺序最好和成员变量的声明顺序保持一致)

4.使用初始化列表的一个注意事项 

类的成员变量类的定义中的声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的书写先后次序无关
比如:

class A
{
public:
	A(int a=1)
		:_a1(a)
		, _a2(_a1)
	{}
	void Print() 
    {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};

int main() 
{
	A aa(1);
	aa.Print();
	return 0;
}

所以代码段中aa对象被创建时,_a2会被赋予随机值(_a2会先完成初始化,_a2初始化时_a1空间中存放的还是随机值)

二.explicit关键字

C++中存在如下的类对象创建方式:

class Date
{
public:
	Date(int year = 0)     构造函数
	{
		_year = year;
        cout << "constructor"<<endl;
	}
    Date(Date& date)       拷贝构造函数
    {
        _year = date._year;
        cout << "copy"<<endl;
    }

private:
	int _year;
};

int main() 
{
	Date a = 2022;         该代码语句发生了隐式类型转换
	return 0;
}

代码段中发生了隐式类型转换: 

然而这种书写方式容易引起误解,因此可以在类的构造函数前加上explicit关键字来禁止这种隐式类型转换的发生。

三.C++类的static成员

1.类中static修饰的成员变量

static修饰的成员变量(静态成员变量)的特性:

  • 静态成员变量的生命周期与全局变量相同(程序启动时创建,结束时销毁)
  • 静态成员变量不占用任何类对象的内存空间(其存在不依赖于任何对象),它被独立地存放在静态区,所有该类的类对象都可以访问该静态成员变量
  • 静态成员变量必须在类外定义,定义时不添加static关键字但是要表明它属于哪个类域.
  • 静态成员变量可用 类名::静态成员 或者 对象.静态成员 来访问

比如:

2.类中static修饰的成员函数

static修饰的成员函数(静态成员函数)的特性:

  • 类静态成员函数可用 类名::静态成员函数 或者 对象.静态成员函数 来访问
  • 静态成员函数的形参中没有隐藏的this指针不能直接访问任何非静态成员变量
  • 静态成员函数不可以直接调用非静态成员函数
  • 非静态成员函数可以直接调用类的静态成员函数
     

比如:

3.相关练习

牛客网JZ64 求1+2+3+...+n 

OJ链接:求1+2+3+...+n_牛客题霸_牛客网

描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0<n≤200
进阶: 空间复杂度 O(1) ,时间复杂度 O(n)

可以借助类的静态成员来解决这个问题:

class Ans
{
public:
    Ans()          每调用一次构造函数就累加一次
    {
        i++;       两个静态变量的生命周期为全局变量的生命周期
        ret+=i;
    }
    int Getans ()
    {
        return ret;
    }
private:
    static int i;
    static int ret;
};
int Ans::i =0;
int Ans:: ret =0;

class Solution 
{
public:
    int Sum_Solution(int n) 
    {
        Ans* ptr = new Ans[n-1];
        Ans ans;          一共创建了n个Ans对象,调用了n次Ans的构造函数
        return ans.Getans();
    }
};

四.类的友元函数和友元类

1.类的友元函数

类的友元函数的概念:

友元函数是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

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

比如现在我们在类外来重载两个运算符:

#include <iostream>

using std::endl;
using std::istream;
using std::ostream;


class Date
{
public:
	friend void operator>>(istream& cin, Date& date);    在类中声明类的友元函数
	friend void operator<<(ostream& cout, Date& date);
private:
	int _year;
};

void operator>>(istream& cin, Date& date)                重载>>运算符
{
	cin >> date._year;
	std::cout << "operator>>" << endl;
}

void operator<<(ostream& cout, Date& date)               重载<<运算符
{
	cout << date._year << endl;
	std::cout << "operator<<" << endl;
}

int main()
{
	Date a;
	std::cin >> a;                                        调用重载的>>运算符
	std::cout << a;                                       调用重载的<<运算符
	return 0;
}

2.类的友元类

class Time
{
friend class Date;    声明Date类为时间类的友元类,则在Date类中就可以直接访问Time类
                      中的私有成员变量
public:
            
private:
    int _hour;
    int _minute;
    int _second;
};


class Date
{
public:
            
    void SetTimeOfDate(int hour, int minute, int second)
    {     
        _t._hour = hour;              直接访问时间类私有的成员变量
        _t._minute = minute;
        _t._second = second;
    }
private:
    Time _t;

};

类的友元类的基本概念:

  • 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
  • 友元关系是单向的,不具有交换性。

比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行

  • 友元关系不能传递

如果C是B的友元, B是A的友元,则不能说明C时A的友元


 

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元能不用就不用。 

五.拷贝对象时的一些编译器优化

给出一段代码:

class A
{
public:
	A(int a = 0)                        A的构造函数
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)                      A的拷贝构造函数
		:_a(aa._a) 
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)           A的赋值运算符重载
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}                        
	~A()                                A的析构函数
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};


void f1(A aa)
{
	;
}


A f2()
{
	A aa;
	return aa;
}



int main()
{
	A a = f2();
	cout << endl;
	return 0;
}

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

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

相关文章

Lesson 4.2 逻辑回归参数估计:极大似然估计、相对熵与交叉熵损失函数

文章目录一、逻辑回归参数估计基本思路1. 构建损失函数2. 损失函数求解二、利用极大似然估计进行参数估计三、熵、相对熵与交叉熵1. 熵&#xff08;entropy&#xff09;的基本概念与计算公式2. 熵的基本性质3. 相对熵&#xff08;relative entropy&#xff09;与交叉熵&#xf…

LeetCode[947]移除最多的同行或同列石头

难度&#xff1a;中等题目&#xff1a;n块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。如果一块石头的 同行或者同列 上有其他石头存在&#xff0c;那么就可以移除这块石头。给你一个长度为 n的数组 stones&#xff0c;其中 stones[i] [xi, yi]…

MATLAB算法实战应用案例精讲-【人工智能】Grover量子搜索算法(补充篇)

前言 因为量子计算的并行性, 搜索问题, 比如说数据库搜索, 最短路径问题, 加密问题, 图形着色问题等, 都被视为可以做到量子加速. Grover 算法,有时也称为量子搜索算法(quantum search algorithm),指一种在量子计算机上运行的非结构化搜索算法,是量子计算的典型算法…

LeetCode[765]情侣牵手

难度&#xff1a;困难题目&#xff1a;n对情侣坐在连续排列的 2n个座位上&#xff0c;想要牵到对方的手。人和座位由一个整数数组 row表示&#xff0c;其中 row[i]是坐在第 i 个座位上的人的 ID。情侣们按顺序编号&#xff0c;第一对是 (0, 1)&#xff0c;第二对是 (2, 3)&…

#A. Balanced Lineup排队(rmq模板题)

题目思路建议先看看详解rmq问题很明显这道题意是跟你一段数列&#xff0c;并给出多次询问,询问区间内最大值和最小值的差。如果去暴力枚举显然会超时,所以要用st算法来解决。我们要建立两个RMQ预处理内容&#xff0c;分别处理最大值和最小值。建一个mx[i][j]代表从i开始,长度为…

精品图表Crack:TeeChart ActiveX version 2023.1

TeeChart ActiveX version 2023 数据可视化专家,Visual Studio.Net、Visual Basic、Visual Studio 6和 IIS / ASP的图表组件 概述 TeeChart Pro ActiveX 图表组件库提供数百种 2D 和 3D 图形样式、56 种数学和统计函数供您选择&#xff0c;以及无限数量的轴和 14 个工具箱组件…

DFS(五)最小轮盘锁

752. 打开转盘锁 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字&#xff1a; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 。每个拨轮可以自由旋转&#xff1a;例如把 9 变为 0&#xff0c;0 变为 9 。每次旋转都只能旋转一个拨轮的一位数字。 锁的初始数字为 0000 &#xff0c;一…

【2023.01.26】定时执行专家 V6.6 兔年春节版 - 更新日志

目录 ◆ 最新版下载链接 ◆ 软件更新日志 – TimingExecutor Full Change Log ▼ 2023-01-23 V6.6 ▼ 2023-01-20 V6.5 ▼ 2022-12-25 V6.4 ▼ 2022-11-15 V6.3 ▼ 2022-10-01 V6.2 ▼ 2022-07-30 V6.1&#xff08;Build 769.30072022&#xff09; ▼ 2022-0…

Linux-Ubuntu入门到精通之远程操作指令

1️⃣shutdown 2️⃣查看或配置网卡信息 3️⃣网卡和IP地址 4️⃣ifconfig 5️⃣ping 6️⃣远程登录和复制文件 7️⃣ ssh 基础&#xff08;重点&#xff09; 8️⃣域名 和 端口号 9️⃣SSH 客户端的简单使用 1️⃣0️⃣Windows 下 SSH 客户端的安装 Putty &#xff1a;http:/…

数据结构 | 海量数据处理 | 位图和哈希切分的常见应用 | 布隆过滤器的使用场景

文章目录位图应用question 1question 2question 3位图的作用哈希切分布隆过滤器作为一种数据结构&#xff0c;哈希桶有着不同于其他数据结构的思想——直接映射&#xff0c;这使得在哈希结构中查找数据的效率达到了最快的O(1)&#xff0c;比起搜索树的比较数据大小&#xff0c;…

数学建模——降维算法

降维 降维的意义 降低无效、错误数据对建模的影响&#xff0c;提高建模的准确性少量切具有代表性的数据将大幅缩减挖掘所需的时间降低存储数据的成本 需要降维的情况 维度灾难。很难有一个简洁的模型在高维空间中依旧具有鲁棒性&#xff0c;而随着模型复杂度的增加&#xf…

【LeetCode每日一题:1663. 具有给定数值的最小字符串~~~递归+DFS+贪心】

题目描述 小写字符 的 数值 是它在字母表中的位置&#xff08;从 1 开始&#xff09;&#xff0c;因此 a 的数值为 1 &#xff0c;b 的数值为 2 &#xff0c;c 的数值为 3 &#xff0c;以此类推。 字符串由若干小写字符组成&#xff0c;字符串的数值 为各字符的数值之和。例如…

你会仲裁吗——劳动仲裁需要准备

目录 劳动仲裁申请书 仲裁思路 ​编辑 一、仲裁前准备 1、自己不认可年终奖 2、自己不认可绩效 二、【仲裁前】协商阶段 1、主张3个月足额年终奖 2、公司协商离职赔偿方案 年终奖仲裁证据 延时加班费 周末加班费 节假日加班费 其他福利-如房补 申请劳动仲裁交通…

uniapp组件传参方式梳理

前言 日常开发中经常会遇到组件之间传递参数问题,整理了几种常见的传参方式,方便日后复习梳理. 常见组件传参的三种方式: 1.父组件向子组件传参 2.子组件向父组件传参 3.子组件向非父组件传参 1.父组件向子组件传参自定义组件: <template><view c…

XPath入门

好久没用XPath了&#xff08;之前没做笔记&#xff09;&#xff0c;最近要用的时候又到处查。。。&#xff08;痛苦.jpg&#xff09;&#xff0c;还是记录下吧&#xff08;π_π&#xff09; XPath&#xff0c;是XML Path Language的缩写&#xff0c; 是一门在 XML 文档中查找…

【Linux】POSIX信号量

目录&#x1f308;前言&#x1f338;1、POSIX信号量&#x1f368;1.1、概念&#x1f367;1.2、PV操作&#x1f33a;2、POSIX信号量相关API&#x1f368;2.1、初始化和销毁信号量&#x1f367;2.2、等待信号量&#xff08;P&#xff09;&#x1f370;2.3、发布信号量&#xff08…

ImmutableList hessian2序列化失败问题分析

问题描述 A服务提供了个RPC接口给B服务使用&#xff0c;入参里有个参数是List类型&#xff0c;B服务在传参时使用Guava里的 ImmutableList&#xff0c;结果发生报错。 其中&#xff0c;B服务即consumer端的异常为&#xff1a;「com.alibaba.dubbo.remoting.RemotingException:…

AD采集之离散化概念(Quantizer模型使用介绍)

模拟量采集的PLC程序和功能块算法,可以参看下面的文章链接: PLC模拟量采集算法数学基础(线性传感器)_RXXW_Dor的博客-CSDN博客_模拟量采集线性校准模拟量采集库如何设计,具体算法代码请参看我的另一篇博文:PLC模拟量输入 模拟量转换FC:S_ITR_RXXW_BOSS的博客-CSDN博客_…

缓存数据库memcached

目录 一.memcached简介 memcached简介 memcached的特点 二.memcached安装 2.1.yum安装 2.2.源码安装 三.memcached命令 3.1.memcached的特征 3.2.memcached的set命令 3.3.memcached的get命令 四.memcached应用实例配置 4.1.图示 4.2.基础配置 4.3.环境规划 4.3.1…

SpringBoot程序的打包和运行

程序打包 SpringBoot程序是基于Maven创建的&#xff0c;在Maven中提供有打包的指令&#xff0c;叫做package。本操作可以在Idea环境下执行。 mvn package​ 打包后会产生一个与工程名类似的jar文件&#xff0c;其名称是由模块名版本号.jar组成的。 程序运行 ​ 程序包打好以…