【C++】类和对象(下):初始化列表、类型转换、Static、友元、内部类、匿名对象/有名对象、优化

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的简介:


C++的两个参考文档:

老朋友(非官方文档):cplusplus

官方文档(同步更新):cppreference



目录

一、只缘构造又重来

[1.1 再探构造:初始化列表](#1.1 再探构造:初始化列表)

[1.2 初始化列表知识点总结](#1.2 初始化列表知识点总结)

[1.3 成员变量走初始化列表的逻辑](#1.3 成员变量走初始化列表的逻辑)

[1.4 例题](#1.4 例题)

[1.5 成员变量](#1.5 成员变量)

[1.5.1 每个成员变量只能出现一次](#1.5.1 每个成员变量只能出现一次)

[1.5.2 成员变量引用](#1.5.2 成员变量引用)

[1.6 显示、缺省初始化](#1.6 显示、缺省初始化)

[1.6.1 显示、缺省](#1.6.1 显示、缺省)

[1.6.2 显示、缺省混用的场景](#1.6.2 显示、缺省混用的场景)

[1.6.3 代码演示](#1.6.3 代码演示)

[1.7 自定义类型/内置类型](#1.7 自定义类型/内置类型)

[1.7.1 自定义类型](#1.7.1 自定义类型)

[1.7.2 内置类型](#1.7.2 内置类型)

[1.8 博主手记](#1.8 博主手记)

二、类型转换

[2.1 类型转换的概念:explicit](#2.1 类型转换的概念:explicit)

[2.2 代码展示](#2.2 代码展示)

三、static成员

[3.1 static成员的概念](#3.1 static成员的概念)

[3.2 【1+2+3+...+n】](#3.2 【1+2+3+...+n】)

[3.2.1 题目描述](#3.2.1 题目描述)

[3.2.2 代码实现](#3.2.2 代码实现)

[3.3 题目](#3.3 题目)

四、友元(friend)------"轻度社交"

[4.1 友元的概念](#4.1 友元的概念)

[4.2 友元重点总结](#4.2 友元重点总结)

[4.3 代码实现](#4.3 代码实现)

[4.4 前置声明](#4.4 前置声明)

[4.5 耦合性:什么样的设计才是好的软件设计](#4.5 耦合性:什么样的设计才是好的软件设计)

五、内部类------不是成员

[5.1 内部类的知识点](#5.1 内部类的知识点)

[5.2 代码演示](#5.2 代码演示)

[6.3 用内部类重新解决【1+2+3+...+n】问题](#6.3 用内部类重新解决【1+2+3+...+n】问题)

六、匿名对象和有名对象

[6.1 知识点](#6.1 知识点)

[6.2 代码演示](#6.2 代码演示)

七、优化------对象拷贝时的编译器优化

[7.1 知识点](#7.1 知识点)

[7.1.1 概念](#7.1.1 概念)

[7.1.2 优化](#7.1.2 优化)

[7.2 优化展示(对比无优化)](#7.2 优化展示(对比无优化))

[7.3 NRVO && URVO](#7.3 NRVO && URVO)

[7.4 博主手记](#7.4 博主手记)

八、本文代码

Test.cpp:

结尾


一、只缘构造又重来

1.1 再探构造:初始化列表

1、之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

2、每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。

3、引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。

4、C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。

5、尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。

6、初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。

1.2 初始化列表知识点总结

(1)" : "------开始,分隔;

(2)无论是否显示写初始化列表,每个构造函数都有初始化列表;

(3)无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化;

(4)如下图所示------

1.3 成员变量走初始化列表的逻辑

1.4 例题

下面程序的运行结果是什么()

A. 输出 1 1

B. 输出 2 2

C. 编译报错

D. 输出 1 随机值

E. 输出 1 2

F. 输出 2 1

代码演示如下:

cpp 复制代码
#include<iostream>
using namespace std;
class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{
	}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2 = 2;
	int _a1 = 2;
};
int main()
{
	A aa(1);
	aa.Print();
}

正确答案:D选项。

1.5 成员变量

1.5.1 每个成员变量只能出现一次

1.5.2 成员变量引用

成员变量引用------跟外面的变量联系起来。

1.6 显示、缺省初始化

1.6.1 显示、缺省

C++11支持在内置类型给缺省值:

显示传用显示,没有用缺省------

1.6.2 显示、缺省混用的场景

1.6.3 代码演示

代码演示如下------

cpp 复制代码
#include<iostream>
using namespace std;

class Time
{
public:
	Time(int hour)
		:_hour(hour)
	{
		cout << "Time()" << endl;
	}
private:
	int _hour;
};

class Date
{
public:
	Date(int& x, int year = 1, int month = 1, int day = 1)
		:_year(100)
	{}
 //C++11支持在内置类型给缺省值
private:
	//声明,缺省值
	int _year = 1;
	int _month = 1;
	int _day;
 //显示传用显示,没有用缺省

	Time _t = 1;
	const int _x = 1;
	int* _ptr = (int*)malloc(40);
};

int main()
{
	Date d1;//没有合适的构造函数可用

	return 0;
}

1.7 自定义类型/内置类型

1.7.1 自定义类型

自定义类型(类类型)会去调用它的对应的默认构造。

1.7.2 内置类型

内置类型(编译器)会不做处理。

1.8 博主手记


二、类型转换

2.1 类型转换的概念:explicit

1、C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

2、构造函数前面加explicit就不再支持隐式类型转换。

3、类类型的对象之间也可以隐式转换,需要相应的构造函数支持。

我们可以用Linux的XShell查看一下有没有优化------

2.2 代码展示

cpp 复制代码
class A
{
public:
	//构造函数explicit就不再支持隐式类型转换
	//explicit A(int a1)
	A(int a1)
		:_a1(a1)
	{ }

private:
	int _a1 = 1;
	int _a2 = 2;
};

void func(const A& aa = 1)
{}

class Stack
{
public:
	void Push(const A& a)
	{ }
};

int main()
{
	int i = 1;
	double d = i;

	const double& ref1 = i;

	//构造
	A a1(1);

	//隐式类型转换
	A a2 = 1;

	const A& ref2 = a1;
	const A& ref3 = 1;

	func(a1);
	func(1);
	func();

	Stack st1;
	A a3(3);
	st1.Push(a3);

	st1.Push(3);

	return 0;
}
//总结:不修改成员变量的成员函数都应该加上const,这样const对象可以调用、普通对象也可以调用

class A
{
public:
	//构造函数explicit就不再支持隐式类型转换
	//explicit A(int a1)
	A(int a1)
		:_a1(a1)
	{
		cout << "A(int a1)" << endl;
	}
	A(const A& aa)
	{
		cout << "A(const A&& aa)" << endl;
	}

	A(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{ }

	int Get() const
	{
		return _a1 + _a2;
	}

private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
public:
	B(const A& a)
		:_b(a.Get())
	{ }
private:
	int _b = 0;
};

int main()
{
	//构造
	A a1(1);

	//2为参数构造临时对象,临时对象拷贝构造a2 -> 优化为直接构造
	A a2 = 2;

	const A& ref1 = 3;

	A a3(1, 1);
	A a4 = (1, 1);
	const A& ref2 = { 1,1 };

	//Stack st1;
	//st1.Push(a4);
	//st1.Push({ 2,2 });

	B b1 = a3;
	const B& ref3 = a3;

	return 0;
}

三、static成员

3.1 static成员的概念

1、用static修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。

2、静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。

3、用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。

4、静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。

5、非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。

6、突破类域就可以访问静态成员,可以通过类名:静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。

7、静态成员也是类的成员,受public、protected、private访问限定符的限制。

8、静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。

3.2 【1+2+3+...+n】

牛客网链接:求1+2+3+...+n

这个等我们接下来介绍完【内部类】的内容之后还会有一种写法,现在先用这个实现------

3.2.1 题目描述

3.2.2 代码实现

代码演示如下------

cpp 复制代码
class Sum
{
    public:
    Sum()
    {
        _ret += _i;
        ++_i;

    }

    static int GetRet()
    {
        return _ret;
    }
    private:
    static int _i;
    static int _ret;
};

int Sum::_i = 1;
int Sum::_ret = 0;

class Solution {
public:
    int Sum_Solution(int n) {
        Sum arr[n];

        return Sum::GetRet();
    }
};

3.3 题目


四、友元(friend)------"轻度社交"

4.1 友元的概念

1、友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。

2、外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。

3、友元函数可以在类定义的任何地方声明,不受类访问限定符限制。

4、一个函数可以是多个类的友元函数。

5、友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。

6、友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。

7、友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。

8、有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

4.2 友元重点总结

1、友元(friend),突破访问限定符(封装的一种方式)的限制------可以访问私有 / 公有;

2、前置声明(如class B;):编译器界定是向上查找------这个设计是为了保证编译速度(设定原因),但这个前置声明只能保证编译器认识class B这个类型,至于里面的细节,编译器是不知道的,比如里面的成员_b1,要做声明和定义的分离;

3、友元是单向的,以社交软件打比方,友元就像是抖音(关注,只是轻度社交关系),而不像是双向关注(朋友圈)的微信(深度社交关系),那怎么办?看第四点------;

4、互相成为友元(谁想访问谁,谁就成为谁的友元)------

成员函数成为友元(要麻烦一点,要指明类域、...等);

5、友元关系不能传递:类似于西欧中世纪的附庸关系------"我附庸的附庸不是我的附庸",------

4.3 代码实现

cpp 复制代码
//前置声明,都则A的友元函数声明编译器声明编译器不认识B
class B;

class A
{
	//友元声明
	friend void func(const A& aa, const B& bb);
private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
	//友元声明
	friend void func(const A& aa, const B& bb);
private:
	int _a1 = 3;
	int _a2 = 4;
};

void func1(const A& aa, const B& bb)
{
	cout << aa._a1 << endl;
	cout << bb._b1 << endl;
}

//xxx.h
class C
{
	//友元声明
	friend class D;
	friend func1(const D& dd);
private:
	int _a1 = 1;
	int _a2 = 2;
};

class D
{
	friend class C;
public:
	void func1(const C& aa);
	void func2(const C& aa);
private:
	int _b1 = 3;
	int _b2 = 4;
};

//xxx.cpp
void C::func1(const D& dd)
{
	cout << dd._b1 << endl;
}

void D::func1(const C& aa)
{
	cout << aa._a1 << endl;
	cout << _b1 << endl;
}

void D::func2(const C& aa)
{
	cout << aa._a2 << endl;
	cout << _b2 << endl;
}

int main()
{
	A aa;
	B bb;
	func(aa, bb);

	C cc;
	D dd;
	dd.func1(cc);
	dd.func2(cc);

	return 0;
}

4.4 前置声明

4.5 耦合性:什么样的设计才是好的软件设计

耦合性(关系太亲近了<-耦合性太高)增加、破坏了封装------友元不宜多。


五、内部类------不是成员

内部类不是成员,定义在类内部,也就是说:"内部类是独立的类"

5.1 内部类的知识点

1、如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。

2、内部类默认是外部类的友元类。

3、内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。

5.2 代码演示

cpp 复制代码
class A
{
private:
	static int _k;
	int _h = 1;
public:
	//内部类
	class B//B默认就是A的友元
	{
	public:
		void foo(const A& a)
		{
			cout << _k << endl;//OK
			cout << a._h << endl;//OK
		}
	private:
		int _b1;
	};
};

int main()
{
	cout << sizeof(A) << endl;

	A::B bb;

	return 0;
}

6.3 用内部类重新解决【1+2+3+...+n】问题

代码演示如下:

cpp 复制代码
//内部类
class Solution {
    class Sum 
    {
    public:
        Sum() 
        {
            _ret += _i;
            ++_i;
        }
    };
  public:
    int Sum_Solution(int n) {
        Sum arr[n];

        return _ret;
    }

  private:
    static int _i;
    static int _ret;
};

int Solution::_i = 1;
int Solution::_ret = 0;

对比前一种写法------


六、匿名对象和有名对象

6.1 知识点

1、用类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名(实参)定义出来的叫有名对象;

2、匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。

6.2 代码演示

cpp 复制代码
class Solution
{
	class Sum
	{
	public:
		Sum()
		{
			_ret += _i;
			++_i;
		}
	};
public:
	void Clear()
	{
		_i = 1;
		_ret = 0;
	}

	int Sum_Solution(int n)
	{
		Sum* ptr = new Sum[n];
		delete[] ptr;

		return _ret;
	}

	~Solution()
	{
		cout << "~Solution()" << endl;
	}

private:
	static int _i;
	static int _ret;
};

int Solution::_i = 1;
int Solution::_ret = 0;

void Func(const Solution& s = Solution(), int i = 1)
{

}

int main()
{
	Solution s;//有名对象
	cout << s.Sum_Solution(10) << endl;
	s.Clear();

	//生命周期只在当前一行
	Solution();//匿名对象
	cout << Solution().Sum_Solution(10) << endl;
	//const 引用会延长匿名对象的生命周期,生命周期和const引用一样
	const Solution& ref = Solution();

	Func(Solution());
	Func(s);
	Func();

	return 0;
}

注意:const 引用会延长匿名对象的生命周期,生命周期和const引用一样。

再来看一段代码,对匿名对象有更进一步的认识------

cpp 复制代码
class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
class Solution {
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};
int main()
{
	A aa1;
	// 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义 
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字, 
	// 但是他的⽣命周期只有这一行,我们可以看到下一行他就会自动调用析构函数 
	A();
	A(1);
	A aa2(2);
	// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说 
	Solution().Sum_Solution(10);
	return 0;
}

七、优化------对象拷贝时的编译器优化

7.1 知识点

7.1.1 概念

1、现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少一些传参和传返回值的过程中可以省略的拷贝。

2、如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新更"激进"的编译器还会进行跨行跨表达式的合并优化。

3、linux下可以将下面代码拷贝到test.cpp文件,编译时用

cpp 复制代码
g++test.cpp-fno-elide-constructors

的方式关闭构造相关的优化。

7.1.2 优化

临时对象比较小,可以存在寄存器;优化就是省略掉之间的临时对象。

7.2 优化展示(对比无优化)

7.3 NRVO && URVO

C++官网下图对应位置的网址:Copy elision

连续一个表达式步骤中的连续拷贝会进行合并优化。

优化前后对比------

这里的原理就是:

原来要构造三个,现在只构造一个------只构造一个,因为这里的aa是aa1的别名。

编译器超前(极致优化),g++也是如此优化的。

优化前后对比------

跨行优化------编译器比文档更加(或者说超前)激进,直接跨行。

7.4 博主手记


八、本文代码

Test.cpp:

cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS  1

#include<iostream>
using namespace std;

//class Time
//{
//public:
//	Time(int hour)
//		:_hour(hour)
//	{
//		cout << "Time()" << endl;
//	}
//private:
//	int _hour;
//};
//
//class Date
//{
//public:
//	Date(int& x, int year = 1, int month = 1, int day = 1)
//		:_year(year)
//		, _month(month)
//		, _day(day)
//		, _t(12)
//		,_ref(x)
//		, _n(1)
//	{
//		//error C2512 :"Time":没有合适的默认构造函数可以用
//		//error C2530 :"Date::_ref" :必须初始化引用
//		//error C2789 :"Date::_n" : 必须初始化常量限定类型的对象
//	}
//	
//private:
//	//声明
//	int _year;
//	int _month;
//	int _day;
//
//	int& _ref;//引用
//	const int _n;//const
//	Time _t;//没有默认构造
//};
//
//class Stack
//{
//public:
//	Stack(int n)
//	{
//		cout << "Stack(int n)" << endl;
//	}
//};
//
//class MyQueue
//{
//public:
//	MyQueue(int n = 4)
//		:_st1(n)
//		,_st2(n)
//	{ }
//private:
//	Stack _st1;
//	Stack _st2;
//};
//
//int main()
//{
//	int y = 0;
//	// 对象整体定义
//	Date d1(y);
//
//	++y;
//
//	MyQueue q1;
//
//	return 0;
//}

//#include<iostream>
//using namespace std;
//
//class Time
//{
//public:
//	Time(int hour)
//		:_hour(hour)
//	{
//		cout << "Time()" << endl;
//	}
//private:
//	int _hour;
//};
//
//class Date
//{
//public:
//	Date(int& x, int year = 1, int month = 1, int day = 1)
//		:_year(100)
//	{}
// //C++11支持在内置类型给缺省值
//private:
	////声明,缺省值
	//int _year = 1;
	//int _month = 1;
	//int _day;
 ////显示传用显示,没有用缺省
//
//	Time _t = 1;
//	const int _x = 1;
//	int* _ptr = (int*)malloc(40);
//};
//
//int main()
//{
//	Date d1;//没有合适的构造函数可用
//
//	return 0;
//}

//class Stack
//{
//public:
//	Stack(int n = 4)
//	{
//		cout << "Stack(int n)" << endl;
//	}
//};
//
//class MyQueue
//{
//public:
//	int _size = 0;
//	Stack _st1;
//	Stack _st2;
//};
//
//class A
//{
//public:
//	A(int n = 10)
//		:_a((int*)malloc(sizeof(int)* n))
//		, _size(0)
//	{
//		//检查
//		if (_a == nullptr)
//		{
//			cout << "malloc fail" << endl;
//			exit(-1);
//		}
//
//		memset(_a, 0, sizeof(int) * n);
//	}
//
//private:
//	int* _a;
//	int _size;
//};
//
//int main()
//{
//	MyQueue q1;
//	A aa;
//
//	return 0;
//}

//class A
//{
//public:
//	//构造函数explicit就不再支持隐式类型转换
//	//explicit A(int a1)
//	A(int a1)
//		:_a1(a1)
//	{ }
//
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//
//void func(const A& aa = 1)
//{}
//
//class Stack
//{
//public:
//	void Push(const A& a)
//	{ }
//};
//
//int main()
//{
//	int i = 1;
//	double d = i;
//
//	const double& ref1 = i;
//
//	//构造
//	A a1(1);
//
//	//隐式类型转换
//	A a2 = 1;
//
//	const A& ref2 = a1;
//	const A& ref3 = 1;
//
//	func(a1);
//	func(1);
//	func();
//
//	Stack st1;
//	A a3(3);
//	st1.Push(a3);
//
//	st1.Push(3);
//
//	return 0;
//}
////总结:不修改成员变量的成员函数都应该加上const,这样const对象可以调用、普通对象也可以调用

//class A
//{
//public:
//	//构造函数explicit就不再支持隐式类型转换
//	//explicit A(int a1)
//	A(int a1)
//		:_a1(a1)
//	{
//		cout << "A(int a1)" << endl;
//	}
//	A(const A& aa)
//	{
//		cout << "A(const A&& aa)" << endl;
//	}
//
//	A(int a1, int a2)
//		:_a1(a1)
//		,_a2(a2)
//	{ }
//
//	int Get() const
//	{
//		return _a1 + _a2;
//	}
//
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//
//class B
//{
//public:
//	B(const A& a)
//		:_b(a.Get())
//	{ }
//private:
//	int _b = 0;
//};
//
//int main()
//{
//	//构造
//	A a1(1);
//
//	//2为参数构造临时对象,临时对象拷贝构造a2 -> 优化为直接构造
//	A a2 = 2;
//
//	const A& ref1 = 3;
//
//	A a3(1, 1);
//	A a4 = (1, 1);
//	const A& ref2 = { 1,1 };
//
//	//Stack st1;
//	//st1.Push(a4);
//	//st1.Push({ 2,2 });
//
//	B b1 = a3;
//	const B& ref3 = a3;
//
//	return 0;
//}

////静态成员函数没有this指针
//class A
//{
//public:
//	A(int a = 0)
//		:_a1(a)
//		, _a2(a)
//	{
//		++_count;
//	}
//
//	A(const A& t)
//	{
//		++_count;
//	}
//
//	static int GetCount()
//	{
//		// _a1++; 不能访问非静态成员,没有this
//		return _count;
//	}
//private:
//	int _a1 = 1;
//	int _a2 = 1;
//public:
//	//声明
//	static int _count;
//};
//
//int A::_count = 0;
// //非静态可以直接访问静态和非静态
//int main()
//{
//	A aa1;
//	cout << sizeof(aa1) << endl;
//	A* ptr = nullptr;
//
//	A aa2 = 1;
//	cout << ptr->_count << endl;
//	cout << aa1._count << endl;
//	cout << A::_count << endl;
//
//	cout << A::GetCount() << endl;
//	cout << aa2.GetCount() << endl;
//	cout << ptr->GetCount() << endl;
//
//	return 0;
//}


//class Sum
//{
//public:
//	Sum()
//	{
//		_ret += _i;
//		++_i;
//	}
//
//	static int GetRet()
//	{
//		return _ret;
//	}
//
//private:
//	static int _i;
//	static int _ret;
//};
//
//int Sum::_i = 1;
//int Sum::_ret = 0;
//
//class Solution
//{
//public:
//	int Sum_Solution(int n)
//	{
//		//变长数组
//		//Sum arr[n];
//		Sum* arr = new Sum[n];
//		delete[] arr;
//		//比C语言的malloc、calloc、realloc、free更超标的来了
//
//		return Sum::GetRet();
//	}
//};
//
////int main()
////{
////	Solution s;
////	cout << s.Sum_Solution(10) << endl;
////
////	return 0;
////}
//
//class A
//{
//public:
//	//构造
//	A()
//	{
//		cout << "A()" << endl;
//	}
//	//析构
//	~A()
//	{
//		cout << "~A()" << endl;
//	}
//};
//
//class B
//{
//public:
//	//构造
//	B()
//	{
//		cout << "B()" << endl;
//	}
//	//析构
//	~B()
//	{
//		cout << "~B()" << endl;
//	}
//};
//
//class C
//{
//public:
//	//构造
//	C()
//	{
//		cout << "C()" << endl;
//	}
//	//析构
//	~C()
//	{
//		cout << "~C()" << endl;
//	}
//};
//
//class D
//{
//public:
//	//构造
//	D()
//	{
//		cout << "D()" << endl;
//	}
//	//析构
//	~D()
//	{
//		cout << "~D()" << endl;
//	}
//};
//
//C c;
//int main()
//{
//	A a;
//	B b;
//	static D d;
//
//	return 0;
//}
//------------------------------------------------------------------------------------------------
////前置声明,都则A的友元函数声明编译器声明编译器不认识B
//class B;
//
//class A
//{
//	//友元声明
//	friend void func(const A& aa, const B& bb);
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//
//class B
//{
//	//友元声明
//	friend void func(const A& aa, const B& bb);
//private:
//	int _a1 = 3;
//	int _a2 = 4;
//};
//
//void func1(const A& aa, const B& bb)
//{
//	cout << aa._a1 << endl;
//	cout << bb._b1 << endl;
//}
//
////xxx.h
//class C
//{
//	//友元声明
//	friend class D;
//	friend func1(const D& dd);
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//
//class D
//{
//	friend class C;
//public:
//	void func1(const C& aa);
//	void func2(const C& aa);
//private:
//	int _b1 = 3;
//	int _b2 = 4;
//};
//
////xxx.cpp
//void C::func1(const D& dd)
//{
//	cout << dd._b1 << endl;
//}
//
//void D::func1(const C& aa)
//{
//	cout << aa._a1 << endl;
//	cout << _b1 << endl;
//}
//
//void D::func2(const C& aa)
//{
//	cout << aa._a2 << endl;
//	cout << _b2 << endl;
//}
//
//int main()
//{
//	A aa;
//	B bb;
//	func(aa, bb);
//
//	C cc;
//	D dd;
//	dd.func1(cc);
//	dd.func2(cc);
//
//	return 0;
//}

//class A
//{
//private:
//	static int _k;
//	int _h = 1;
//public:
//	//内部类
//	class B//B默认就是A的友元
//	{
//	public:
//		void foo(const A& a)
//		{
//			cout << _k << endl;//OK
//			cout << a._h << endl;//OK
//		}
//	private:
//		int _b1;
//	};
//};
//
//int main()
//{
//	cout << sizeof(A) << endl;
//
//	A::B bb;
//
//	return 0;
//}

//class Solution
//{
//	class Sum
//	{
//	public:
//		Sum()
//		{
//			_ret += _i;
//			++_i;
//		}
//	};
//public:
//	void Clear()
//	{
//		_i = 1;
//		_ret = 0;
//	}
//
//	int Sum_Solution(int n)
//	{
//		Sum* ptr = new Sum[n];
//		delete[] ptr;
//
//		return _ret;
//	}
//
//	~Solution()
//	{
//		cout << "~Solution()" << endl;
//	}
//
//private:
//	static int _i;
//	static int _ret;
//};
//
//int Solution::_i = 1;
//int Solution::_ret = 0;
//
//void Func(const Solution& s = Solution(), int i = 1)
//{
//
//}
//
//int main()
//{
//	Solution s;//有名对象
//	cout << s.Sum_Solution(10) << endl;
//	s.Clear();
//
//	//生命周期只在当前一行
//	Solution();//匿名对象
//	cout << Solution().Sum_Solution(10) << endl;
//	//const 引用会延长匿名对象的生命周期,生命周期和const引用一样
//	const Solution& ref = Solution();
//
//	Func(Solution());
//	Func(s);
//	Func();
//
//	return 0;
//}

class A
{
public:
	A(int a = 0)
		:_a1(a)
	{
		cout << "A(int a)" << endl;
	}

	A(const A& aa)
		:_a1(aa._a1)
	{
		cout << "A(const A& aa)" << endl;
	}

	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a1 = aa._a1;
		}
		return *this;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a1 = 1;
};

void f1(A aa)
{ }

//int main()
//{
//	//构造+拷贝构造 优化 -> 构造
//	A aa1 = 1;
//	cout << "=================" << endl;
//
//	f1(aa1);
//	cout << "=================" << endl;
//
//	f1(1);
//	cout << "=================" << endl;
//
//	f1(A(1));
//	cout << "=================" << endl;
//
//	return 0;
//}

//C++官网:http://en.cpprefrence.com/w/cpp/language/copy elision.html
//A f2()
//{
//	//NOVO
//	A aa;
//	cout << &aa << endl;
//	return aa;
//
//	//URVO
//	return A(1);
//}
//
//int main()
//{
//	A aa1 = f2();
//	cout << &aa1 << endl;
//
//	return 0;
//}

A f2()
{
	//NOVO
	A aa;
	cout << &aa << endl;
	return aa;

	//URVO
	return A(1);
}

int main()
{
	//不推荐
	A aa1;
	aa1 = f2();
	cout << &aa1 << endl;

	//推荐
	A aa2 = f2();

	return 0;
}

结尾

往期回顾:

【C/C++】类和对象(中):(二)类的默认成员函数------拷贝构造,赋值拷贝------赋值运算符重载

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
风_峰2 小时前
PuTTY软件访问ZYNQ板卡的Linux系统
linux·服务器·嵌入式硬件·fpga开发
数智顾问2 小时前
从ENIAC到Linux:计算机技术与商业模式的协同演进——云原生重塑闭源主机,eBPF+WebAssembly 双引擎的“Linux 内核即服务”实践
linux
疋瓞2 小时前
C++_STL和数据结构《1》_STL、STL_迭代器、c++中的模版、STL_vecto、列表初始化、三个算法、链表
数据结构·c++·算法
-SGlow-2 小时前
Linux相关概念和易错知识点(45)(网络层、网段划分)
linux·运维·服务器·网络
三体世界2 小时前
测试用例全解析:从入门到精通(1)
linux·c语言·c++·python·功能测试·测试用例·测试覆盖率
过尽漉雪千山2 小时前
Flink1.17.0集群的搭建
java·大数据·linux·flink·centos
Bear on Toilet3 小时前
继承类模板:函数未在模板定义上下文中声明,只能通过实例化上下文中参数相关的查找找到
开发语言·javascript·c++·算法·继承
csdn_aspnet3 小时前
Windows、Linux 系统 nodejs 和 npm 版本更新及错误修复
linux·windows·npm·node.js
潘潘潘潘潘潘潘潘潘潘潘潘3 小时前
【MySQL】从零开始学习MySQL:基础与安装指南
linux·运维·服务器·数据库·学习·mysql