首页 > 编程语言 >【c++篇】:解析c++类--优化编程的关键所在(三)

【c++篇】:解析c++类--优化编程的关键所在(三)

时间:2024-10-25 19:17:37浏览次数:3  
标签:初始化 -- 成员 编程 c++ month int year day

感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢
个人主页:余辉zmh–CSDN博客
文章所属专栏:c++篇–CSDN博客

在这里插入图片描述

文章目录

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

1.1构造函数体的赋值

在上一篇的文章中关于构造函数我们知道,当我们在创建对象时,编译器会调用构造函数对成员变量进行初始化,比如下面这段代码:

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

private:
	int _year;
	int _month;
	int _day;
};
int main(){
    Date d1(2024,10,24);
    return 0;
}

在上面这段代码中,创建对象d1调用构造函数完成初始化,虽然传参数使对象d1的成员变量有了一个初始值,但是这时候并不能称为对对象成员变量的初始化,而是对成员变量进行赋初值,在之后依然可以多次赋值。**而初始化只能初始化一次。**因此就需要使用初始化列表来完成初始化。

1.2初始化列表

初始化列表是c++中构造函数的一部分,用于初始化类的成员变量。在构造函数中,初始化列表可以用于初始化常量成员变量引用成员变量,或者其他自定义类型的成员变量

初始化列表的语法如下:

classname::classname(参数列表)
    :初始化列表
{
    构造函数体
}

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

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

private:
	int _year;
	int _month;
	int _day;
};
int main(){
    Date d1(2024,10,24);
    return 0;
}

下面是几个关于初始化列表需要注意的点:

  • 每个变量在初始化列表中只能出现一次(也就是只能初始化一次)

  • 当类中包含常量成员变量引用成员变量以及自定义类型成员变量(该类中没有默认构造函数)时必须放到初始化列表位置进行初始化

    class A{
    public:
        A(int a)
            :_a=a
        {}
    private:
        int _a;
    };
    class B{
    public:
        B(int a,int ret)
            :_obj(a)
            ,_ret(ret)
            ,_n(10)
        {}
    private:
        //自定义类型成员变量且对象类中没有默认构造函数
        A _obj;
        //引用成员变量
        int& _ret;
        //常量成员变量
        const int _n;
    };
    
  • 对于自定义类型来说,一定会使用初始化列表初始化,所以尽量使用初始化列表完成初始化

    以下面这段代码为例:

    class Time {
    public:
    	Time(int hour=0)
    		:_hour(hour)
    	{
    		cout << "Time(int hour=0)" << endl;
    	}
    private:
    	int _hour;
    };
    
    class Date {
    public:
    	Date(int year=1, int month=1, int day=1)
    		:_year(year)
    		,_month(month)
    		,_day(day)
    	{}
    private:
        //自定义类型成员变量调用对应类的构造函数初始化列表
    	Time _t;
    	int _year;
    	int _month;
    	int _day;
    };
    int main() {
    	Date d1;
    	return 0;
    }
    
    

在这里插入图片描述

  • 成员变量在初始化列表中的初始化顺序是按照类中声明的顺序,与其在初始化列表中的前后次序无关

    我们通过一段代码来看看这个例子:

    class A {
    public:
    	A(int a)
    		:_a1(a)
    		,_a2(_a1)
    	{}
    	void Print() {
    		cout << "_a1:" << _a1 << endl;
    		cout << "_a2:" << _a2 << endl;
    	}
    private:
    	int _a2;
    	int _a1;
    };
    
    int main() {
    	A aa1(1);
    	aa1.Print();
    	return 0;
    }
    

    在这里插入图片描述

    在上面这段代码中,可以看到输出结果为a1为1,a2为随机值,起初可能我们会以为,输出结果都为1,因为我们会觉得按照初始化列表顺序是a1先初始化为1,然后a2再初始化为1。

    但事实上是按照声明中的顺序,a2先初始化为a1,但此时,a1还是随机值,所以a2就成了随机值,然后a1再初始化为1

    因此从上面的例子中我们就可以知道成员变量在初始化列表中的初始化顺序是按照类中声明的顺序,与其在初始化列表中的前后次序无关

1.3explicit关键字

explicit关键字在c++中用于修饰类的构造函数,其主要目的是防止构造函数在默认情况下被隐式的用于类型转换。当构造函数只接受单个参数时(或者全部参数都有默认值),并且没有被声明为explicit时,他既可以作为构造函数是用来创建对象,也可以作为转换函数使用,参数类型隐式的转换为类类型。

接受单个参数的构造函数的具体体现:

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

	Date& operator=(const Date& d) {
		if (this != &d) {
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

	void Print() {
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1 = 2023;
	d1.Print();
	return 0;
}

这里是隐式类型转换整形转换为自定义类型,整形2023构造一个d1的临时对象,临时对象再拷贝构造对象d1。如果在构造函数函数名前面加explicit关键字,就会禁止单参构造函数类型转换的作用

explicit Date(int year=1, int month=1, int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

在这里插入图片描述

二.静态static成员

在c++中,静态成员变量和静态成员函数时类的成员,但与类的任何对象实例多无关,他们只属于类的本身,而不是类的任何对象

2.1.静态成员变量

  • 定义:静态成员变量属于类的每个对象共享的变量,存储在静态区。
  • 初始化:静态成员变量在类中声明,在类外部定义也就是进行初始化,注意不能在类的构造函数中初始化。
  • 访问:可以通过类名和域作用限定符::来访问静态成员变量,也可以通过类的对象来访问。

2.2.静态成员函数

  • 定义:静态成员函数可以在没有类对象的情况下调用,可以访问静态成员变量和静态成员函数,但是不能访问非静态成员变量和非静态成员函数(因为不属于任何类的对象)。
  • 特点:没有this指针,因为this指针指向调用它的对象,而静态成员函数不依赖任何对象;不能用const修饰,因为const修饰的成员函数不能修改任何成员变量,但是静态成员函数没有任何对象与之关联。
  • 访问:可以通过类名和域作用限定符::来调用,也可以通过类的对象来调用。

这里通过一道例题来演示静态成员的使用。“实现一个类,计算程序创建了多少个类对象”:

class A {
public:
    //构造函数
	A() {
		_scout++;
	}
    //拷贝构造函数
	A(const A& a) {
		_scout++;
	}
    //析构函数
	~A() {
		_scout--;
	}
    //静态成员函数用来返回私有静态成员变量的值
	static int Getscout() {
		return _scout;
	}
private:
    //声明一个静态成员变量用来计数
	static int _scout;
};
//静态成员变量在类中声明,在全局定义,定义时要在变量名前加类名和域作用限定符::
int A::_scout = 0;

int main() {
    //通过类名和域作用限定符来调用静态成员函数
	cout << A::Getscout() << endl;
	A a1, a2;
	cout << A::Getscout() << endl;
	A a3(a1);
	cout << A::Getscout() << endl;
	return 0;
}

在这里插入图片描述

三.友元

在c++中友元是一种特殊的类成员关系,它允许一个或多个函数或类访问另一个类的私有和保护成员。友元关系不是一种继承关系,而是一种访问控制上的特权授予。

3.1.友元函数

这里通过讲解重载operator<<来讲述友元函数的使用。(在我上一篇文章中有关于运算符重载的讲解,不清楚的可以看我上一篇文章噢)。

当我们尝试去重载operator<<为成员函数时,我们就会发现,当我们使用<<时,需要打印对象d1<<左边,cout<<右边。而我们平常使用<<时,则是cout在左边,打印对象在右边,使用的时候会感觉非常别扭。之所以会这样是因为,在重载operator<<为成员函数时,this指针默认占了第一个参数的位置,因此,使用<<的对象就成为了左操作数,也就是需要在<<的左边。但如果将operator<<重载成全局函数时,又会导致类外无法访问成员,这时候就需要友元函数来解决。

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

	ostream& operator<<(ostream& _cout) {
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2024, 10, 24);
    //<<运算符重载
	d1 << cout;
	return 0;
}

友元函数可以直接访问类的私有成员,他是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加上friend关键字。

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

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

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout,const Date& d) {
	_cout << d._year<< "-" << d._month << "-" << d._day << endl;
	return _cout;
}

int main() {
	Date d1(2024, 10, 24);
	cout << d1;
	return 0;
}

注意点:

  • 友元函数可以访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类的任何地方声明,不受访问限定符限制
  • 一个函数可以是多个类的友元函数

3.2.友元类

友元类是在一个类中声明为friend的另一个类。声明后,友元类的所有成员函数都可以访问该类的私有和保护成员。

以下面的代码为例:

Time类中声明Date类为其友元类,那么Date类中的成员函数可以直接访问Time类的私有成员变量,但Time中不能访问Date类的私有成员变量。

class Time {
public:
	friend class Date;

	Time(int hour=1,int minute,int second)
		:_hour(hour)
		,_minute(minute)
		,_second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date {
public:
	Date(int year = 0, int month = 0, int day = 0) 
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	void Getdateoftime(int hour, int minute, int second) {
        //直接访问Time类的私有成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

注意点:

  • 友元关系是单向的,不具有交换性
  • 友元关系不能传递,比如B是A的友元,C是B的友元,不能说明C是A的友元
  • 友元关系不能继承(继承将会在之后讲解,现在只需了解一下就行)

四.内部类

如果一个类定义在一个类的内部,这个类就叫做内部类,内部是一个独立的类,他不属于外部类,更不能通过外部类的对象去访问内部类的成员。

内部类天生是外部类的友元类

  • 内部类可以定义在外部类的任何位置
  • 内部类可以直接访问外部类的静态成员,不需要外部类的对象或类名
  • sizeof(外部类)=外部类,和内部类没有关系
class A {
private:
	static int k;
	int _h;
public:
	A(int h=0)
		:_h(h)
	{}
	class B {
	public:
		void foo(const A& a) {
			cout << k << endl;
			cout << a._h << endl;
		}
	};
};
int A::k = 1;

int main() {
	A::B b;
	A a1(0);
	b.foo(a1);
	return 0;
}

在这里插入图片描述
以上就是关于c++类最后部分的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

标签:初始化,--,成员,编程,c++,month,int,year,day
From: https://blog.csdn.net/2301_82347435/article/details/143220329

相关文章

  • C++11中lambda表达式与包装器
    目录1.lambda表达式1.1引入lambda表达式1.2lambda表达式用法1.3函数对象与lambda表达式2.包装器2.1function包装器2.2bind1.lambda表达式在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。//lambda表达式#include<iostream>#inc......
  • java多线程
    Java中的多线程是Java编程语言的一个重要特性,它允许程序同时执行多个任务。在多线程环境中,每个线程都可以独立地执行任务,提高了程序的并发性和性能。本文将详细介绍Java中多线程的概念、实现方式以及多线程编程中的注意事项。一、多线程的概念多线程是指在一个程序中同时运......
  • MySQL-事务
    目录事务简介事务操作事务四大特性ACID并发事务问题1.脏读2.不可重复读3.幻读事务隔离级别1.读未提交Readuncommitted2.读已提交Readcommitted3.可重复读RepeatableRead(默认)4.串行化Serializable隔离级别与一致性的关系事务简介事务是一组操作的集合,它是......
  • SpringMVC7-RESTful
    目录RESTful简介资源资源的表述状态转移RESTful的实现案例RESTful简介REST:RepresentationalStateTransfer,表现层资源状态转移资源资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象......
  • 【JavaEE初阶】网络原理(1)
    欢迎关注个人主页:逸狼创造不易,可以点点赞吗~如有错误,欢迎指出~互联网中最主流的时TCP/IP五层协议(应用层,传输层,网络层,数据链路层,物理层),应用层是程序员日常开发中最常用到的一层(可以使用已经开发好的协议,也可以自己定义应用层协议),其他层则操作系统/硬件/......
  • 分位数 四分位距 分位数回归
    分位数  四分位距  分位数回归分位数(Quantile),亦称分位点,是指将一个随机变量的概率分布范围分为几个等份的数值点,常用的有中位数(即二分位数)、四分位数、百分位数等。分位数指的就是连续分布函数中的一个点,这个点对应概率p。若概率0<p<1,随机变量X或它的概率分布的分位数Za,是......
  • 如何导入导出浏览器indexedDB数据库中的数据
    varDB;varDBName='';varTableName='';indexedDB.open(DBName).onsuccess=asyncfunction(event){DB=event.target.result;};//导入letdata=JSON.parse(fileData);//json字符串形式的数据letresult=awaitinput(DB,data);consol......
  • 项目经理如何维护项目团队的健康和福祉
    项目经理在维护项目团队的健康和福祉方面扮演着至关重要的角色,他们需要通过建立良好的沟通渠道、提供成长与培训机会、确保合理的工作负荷、创造一个支持性与包容性的工作环境来达成这一目标。建立良好的沟通渠道是维护团队健康与福祉的首要步骤,它帮助团队成员感觉到被尊重和倾听......
  • 第9课 数据库
    一、数据库介绍1、什么是数据库?定义:数据库是存放数据的电子仓库。2、是以某种方式存储百万条,上亿条数据,提供多个用户访问共享。3、每个数据有一个或多个api用于创建,访问,管理和复制所保存的数据。4、系统中很多动态数据都存储在数据库中,需要通过访问数据库才能显示;二、数据库......
  • 文件管理器
    Description文件管理器操作系统具有对计算机硬件资源管理和调度的功能。文件是对占用了硬盘一定空间的对象的描述和抽象。考虑一般的文件具有文件名、大小和创建时间。文件管理在任何操作系统中都是必不可少的。文件管理器是用户用来观察和操作文件的一个软件。考虑一个简易的......