【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化

这篇具有很好参考价值的文章主要介绍了【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 再谈构造

1.1 成员变量的初始化(初始化列表)

1.2 初始化列表的行为

1.3 explicit关键字

 2. 类中的static成员

2.1 静态成员变量

2.2 静态成员函数

3. 友元

3.1 友元函数

3.1 友元类

4. 内部类

 5. 匿名对象

 6. 对象拷贝时候的编译器优化

 文章来源地址https://www.toymoban.com/news/detail-646858.html

1. 再谈构造

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;
};

        而真正的初始化是在初始化列表中进行的,初始化列表的位置是在构造函数”{}“的前面,是由一个冒号后面跟着多个用逗号隔开的成员变量,每个成员变量后面都有括号,括号里面就是初始值,初始化的次序与成员列表次序无关,是按照成员的声明顺序进行初始化,如下:

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

        而且像下面三种成员必须使用初始化列表进行初始化:

  1. const 成员变量:因为const成员具有常性不能进行赋值,而函数体是进行赋值的,所以只能在初始化列表进行初始化(具体与初始化列表的行为有关,待会叙述
  2. 引用成员变量:因为引用在定义的时候必须初始化,并且引用的实体不能更改,如果在函数体里进行,本质上是一个赋值重载。
  3. 没有默认构造的自定义类型:因为自定义类型会调用默认构造进行初始化,如果没有构造就必须显示的初始化。

1.2 初始化列表的行为

        我想大家应该都很困惑,为什么在初始化列表里面是初始化而在函数体里面就是赋值?初始化列表的执行时间是在函数体之前的,当我们调用构造函数的时候,编译器会首先去按照声明顺序进行默认的初始化,如果在初始化列表中有构造初始值则不会执行默认初始化,而去执行显式的初始化,所以无论我们有没有去写初始化列表,都是会在函数体执行之前初始化,这也就解释了为什么有些成员只能在初始化列表里面进行初始化和初始化列表的顺序与初始化顺序无关。而初始化列表对于编译器只是一个参考,可以认为编译器在执行的时候如果你(列表)有,我就按你的来,如果没有我就按照我自己的来。而const成员一旦执行了初始化就不能进行赋值,所以在语法上规定了const必须人为的给构造初始值,这也就是为什么const必须在初始化列表进行初始化了。

        而当我们执行到函数体的时候所有成员其实早就执行默认初始化初始化完成了,这个时候去进行所谓的”初始化“其实都是赋值(拷贝或者进行赋值重载)。

        无论什么时候我们都建议使用初始化列表进行初始化,首先就是不容易出错,其次就是规则上的统一。

1.3 explicit关键字

        如果构造函数只有单个参数或者除第一个参数无默认值其余均有默认值,在一些情况下会存在隐式类型转换。比如

class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};
//
class B
{
public:
	B(int a, int b = 1, int c = 1)
		:_a(a)
        ,_b(b)
        ,_c(c)
	{}
private:
	int _a;
	int _b;
	int _c;
};

int main()
{
	A a1 = 1;  //这俩种情况都会发生隐式类型转换 转换成一个无名类型的对象进行拷贝
	B b1 = 1;
    //C++11
    B b2 = { 1, 2, 3}; //c++11支持的括号初始化,将数组进行类型转换再构造,            
                       //想了解可以看我的c++11专栏


}

【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化,C++,c++,开发语言

         对于代码中提到的括号初始化可以看我的这一篇文章:【C++11】 统一的列表初始化( {}初始化 )_子亦半截诗的博客-CSDN博客

        回归正题,上述的这种类型转换有的时候我们并不希望发生,所以就有了explicit关键字,用这个关键字修饰构造函数,将不会发生这种类型转换,同时C++11的括号初始化特性也会被禁用。

class A
{
public:
	explicit A(int a)
		:_a(a)
	{}
private:
	int _a;
};
//
class B
{
public:
	explicit B(int a, int b = 1, int c = 1)
		:_a(a)
		, _b(b)
		, _c(c)
	{}
private:
	int _a;
	int _b;
	int _c;
};
int main()
{
	A a1 = 1;  //报错:E0415	不存在从 "int" 转换到 "A" 的适当构造函数
	B b1 = 1;  //报错:E0415	不存在从 "int" 转换到 "B" 的适当构造函数
	//C++11
	B b2 = { 1, 2, 3 };   //报错:E2334	复制列表初始化不能使用标记为“显式”的构造函数
}

 报错:【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化,C++,c++,开发语言

 

 2. 类中的static成员

        类中的静态成员和类中其他的成员有很大的区别,非静态成员变量的初始化在初始化列表里面进行,但是静态成员变量不能在初始化列表中初始化,因为static成员并不属于某个对象,static成员被存放在静态区,被同类型的所有对象共享

2.1 静态成员变量

        静态成员变量属于同类型的所有对象,它的定义和初始化在类外进行。在定义的时候需要在变量名前面加上 类名:: 。

int A::_a = 0;
class A
{
public:
private:
	static int _a;
};

2.2 静态成员函数

        由于静态函数属于每一个类对象,而不属于某个对象,所以静态成员函数也就没有this指针,所以也就不能访问类的非静态成员。非静态成员函数可以调用静态成员函数,但是静态成员函数不能调用非静态函数。可能有人要疑问了:非静态成员函数不也是被共享的吗,为什么不能被静态成员函数访问呢?这是因为非静态成员函数需要传入this指针,而静态成员函数是没有this指针的。

int A::_a = 0;
class A
{
public:
	static void test() {  }
	void test1() { test(); }
private:
	static int _a;
};

3. 友元

        友元是一种突破封装的方式,让外部成员访问自己私有成员的一种方式,但过度使用会破坏封装。

3.1 友元函数

        友元函数的声明就是在类中声明函数时在前面加上friend关键字,声明成友元函数这个函数就能访问对象的私有成员。在一些特殊情况下需要使用,就比如”<<“流写入符号重载,由于"<<"的左操作数必须是ostream对象,所以第一个参数就必须是ostream类型,但是如果定义成成员函数,第一个参数就变成this指针了,这会导致我们使用”<<“的时候就变成” 对象<<cout  “,这与我们的习惯就不符,所以流写入的符号重载必须定义成全局函数,而且还必须要能访问类的私有成员,这个时候友元函数就可以发挥作用了。如下:

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, 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;
}
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}
  •  友元函数可以访问类的所有成员,但不是类的成员
  • 友元函数不可以用const修饰
  • 友元函数不受访问限定符限制

3.1 友元类

        如果一个类A声明了一个友元类B,那么B类中的所有成员都可以访问A类的所有成员,但A类不能访问B类的成员。友元关系不能传递和继承(继承以后再说),比如C是D的友元,D是E的友元,但C不是E的友元。

class A
{
	friend class B;
private:
	int _aa;
	int _ab;
	int _ac;
};
class B
{
	void print()
	{
		cout << _atest._aa << ' ' << _atest._ab << ' ' << _atest._ac << endl;
	}

private:
	A _atest;
};

4. 内部类

        当一个类定义在另一个类的内部,这个类就被称之为内部类。内部类相当于外部类的友元类,可以通过对象参数访问内部类中的所有成员,访问静态成员的时候不需要对象参数就可以直接访问,但是外部类不能访问内部类的成员。内部类是一个独立的类,对外部类的大小没有任何影响。外部成员对内部类的访问会受到访问限定修饰符的限制。

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

 5. 匿名对象

        匿名对象的定义就是在对象后面加上一个括号,匿名对象不用取名字,声明周期也只有一行,使用完后就自动调用析构函数销毁了。

class A
{
public:
	static int _a;
};
int A::_a = 0;

int main()
{
	A();   //匿名对象
	cout << A()._a;
}

 6. 对象拷贝时候的编译器优化

        在我们传参或者传值返回的时候可能会进行频繁的构造和拷贝构造,如果对象较大,会造成极大的资源消耗,所以为此编译器做出一些优化。(只针对构造,赋值重载不会进行优化)

class A
{
public:
	A(int a = 0)
	{
		cout << "A(int a = 0)" << endl;
	}
	A(const A& aa)
	{
		cout << "A(const A & aa)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
};
void f1(A a)
{}
A f2()
{
	A a;
	return a;
}
int main()
{
	A a;
	f1(1);  //连续构造+拷贝构造->直接构造
	f1(A(2)); //连续构造+拷贝构造->直接构造
	A a1 = f2(); //拷贝构造+拷贝构造->优化为一个拷贝构造
}

【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化,C++,c++,开发语言

 

 

到了这里,关于【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++学习】类和对象--多态【待补充】

    多态是C++面向对象三大特性之一 静态多态:         函数重载和运算符重载属于静态多态,复用函数名(函数地址早绑定,编译阶段确定函数地址) 动态多态:         派生类和虚函数实现运行时多态(函数地址晚绑定,运行阶段确定函数地址)  案例: 输出:  但

    2024年02月02日
    浏览(26)
  • 【C++】类与对象(2补充运算符重载,const成员)

    作者:爱写代码的刚子 时间:2023.5.11 本篇作为【C++】类与对象(2)的补充(运算符重载、const成员) 赋值运算符重载 运算符重载 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类

    2024年02月04日
    浏览(30)
  • [C++ ]:5.类和对象中(运算符重载补充)+ 类和对象下(初始化列表)

    我们知道进行运算符重载这个函数的参数的左右类型是非常重要的,我们尝试在类中去定义这个流插入重载! 1. 考虑到隐含的参数指针: 2.进行优化! 我们观察上面的代码发现可以实现在类中进行流插入运算符的一个重载但是我们需要考虑隐含参数的位置所以我们进行传参

    2024年02月06日
    浏览(38)
  • C++ | 探究拷贝对象时的一些编译器优化

    👑作者主页:@烽起黎明 🏠学习社区:烈火神盾 🔗专栏链接:C++ 在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的 经过深度探索类的六大天选之子学习,我们讲到了拷贝构造一些基本概念和调用形式 经过构造函数

    2023年04月19日
    浏览(49)
  • C嘎嘎~~[类和对象 精华篇]

    🗨️[题目] 实现一个类,计算程序中创建出了多少个类对象 先分析一下题目: 程序运行中, 有对象的创建, 也有对象的销毁 ⇒ 对应下来就是 构造,拷贝构造 和 析构 对象创建出来要初始化 — — 构造和拷贝构造, 对象到了生命周期就要销毁 — — 析构 那我们可以用一个 全局变

    2024年02月06日
    浏览(30)
  • 用人话讲C++————类与对象的知识进阶(4)

    在日常生活中,一个物品可能由多个零部件组成。比如,一台汽车由发动机、车轮、座椅组成,一只手机由芯片、外壳和显示屏组成。其中车轮、座椅、芯片、显示屏本身也是独立的个体,概括起来就是:一个对象中包含了其他对象。 对象成员简单地来说就是再定义一个新类

    2024年02月12日
    浏览(28)
  • C++类和对象基础知识详解

    1.一切皆是对象。如下图: ·女人是一个对象 ·鞋子是一个对象 ·包包是一个对象 ·衣服是一个对象 ·裤子是一个对象 ·手链是一个对象 … 这里人这个对象,由鞋子、包包、衣服、 裤子、手链等对象组成。 3.每个对象都有: a)数据(描述对象的属性) b)函数(行为操作代码

    2023年04月12日
    浏览(34)
  • 【C++系列P3】‘类与对象‘-三部曲——[基础知识](1/3)

    前言 大家好吖,欢迎来到 YY 滴 C++系列 ,热烈欢迎! 【 \\\'类与对象\\\'-三部曲 】的大纲主要内容如下 : 如标题所示,本章是【 \\\'类与对象\\\'-三部曲 】三章中的第 一 章节—— 基础知识章节 ,主要内容如下: 目录 一. This指针 1.编译器对This指针的处理本质——不允许修改this,但

    2024年02月11日
    浏览(36)
  • C++ | 一些你所忽略的类和对象小知识

    在深度探索类的六大天选之子中,我们学习了类和对象的构造函数,知道了其可以用来初始化成员变量,也学了一些它的相关语法特性,但是C++中的构造函数真的就只是这样吗?本模块我们继续来谈谈有关构造函数的一些知识点 引入 我们知道,对于下面这个类A的成员变量

    2024年02月04日
    浏览(31)
  • 【javaSE】内部类(来自类和对象的补充)

    hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥💥,如果发现这篇文章有问题的话,欢迎各位评论留言指正,大家一起加油!一起chin up!👍👍  💥 个人主页 :E绵绵的博客 💥 所属专栏: JAVASE题目练习

    2024年04月27日
    浏览(19)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包