类和对象(下)

初始化列表

现在除了之前我们讲的构造函数来初始化对象,现在有多了一个初始化列表。

为什么要有初始化列表

初始化列表我们会认为是成员变量定义的地方
这句话很绕,慢慢往下看。

对于对象而言。成员变量定义的地方又是什么意思?

class A
{
public:

private:
//这是声明
	int _a1;
	int _a2;
};
int main()
{
//这是定义
	Date A;
	return 0;
}

但这是对对象整体的定义。每个成员什么时候定义呢?
对象整体定义,每个成员不就定义了吗?道理是这样,但是这样行不通,容易走出歪路子。

class A
{
public:

private:
//这是声明
	int _a1;
	int _a2;
	const int _x;
};
int main()
{
//这是定义
	Date A;
	return 0;
}

加了就报错,提示没有合适的构造函数可用,为什么?

const int _x;

const变量有什么区别?它必须在定义的位置初始化,因为它只有一次初始化的机会,后面就不能改了。

所以const变量必须要处理,后面就没机会了,那在什么地方处理?
1,c++11支持缺省值,在声明的地方给个缺省值就可以了。
2.c++11之前,怎么解决?
必须给每个成员变量找一个定义的位置,不然像const这样的成员不好出理。
所以构造函数就增加了初始化列表

初始化列表格式

冒号开始,逗号分割

class A
{
public:
	A()
		:_x(1)
	{
		_a1++;
	}

private:
	int _a1=1;
	int _a2=1;
	const int _x;
};
int main()
{
	A aa;
	return 0;
}

类和对象(下)_第1张图片

1、哪个对象调用构造函数,初始化列表是它所有成员变量定义的位置
2、不管是否显示在初始化列表写,那么编译器每个变量都会初始化列表定义初始化

用法

缺省值是没给的时候才用它
看下面的代码结果是多少。

class A
{
public:
	A()
		:_x(1)
		,_a2(2)
	{
		_a1++;
		_a2--;
	}

private:
	int _a1=1;
	int _a2=1;
	const int _x;
};
int main()
{
	A aa;
	return 0;
}

在这里插入图片描述

什么时候用初始化列表

c++规定有三个东西必须在初始化列表规定。

1.const成员变量
2.引用成员变量
3.自定义类型成员(且该类没有默认构造函数时)

class B
{
public:
	B(int b)
		:_b(0)
	{
		cout << "B()" << endl;
	}
private:
	int _b;
};
class A
{
public:
		A()
			:_x(1)
			, _ref(_a1)
			, _bb(0)
		{
			_a1++;
			_a2--;
		}
	
	private:
		int _a1 = 1; // 声明
		int _a2 = 2;
		const int _x;
		int& _ref;
		B _bb;//编译器对内置类型不做处理,如果有缺省值那就有缺省值
			  //默认生成的构造函数对内置类型不做处理,对于内置类型就调用它的默认构造
			  //但是它只能调用默认构造,就是不传参数的就可以用的构造函数
	};

所有的成员能在初始化列表初始化最好就在初始化列表初始化

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

class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机

类和对象(下)_第2张图片

explicit关键字

class A
{
public:
	A(int a)
		:_a1(a)
	{

	}
	A(const A& a)
	{
		
	}
private:
	int _a1;
	int _a2;
};
A aa1(1)//直接调用构造函数
A aa2=1 // 隐式类型转换   构造+拷贝+优化->构造

1是整型变量怎么赋值给aa2,这里是发生了类型转换,首先用1去构造一个临时对象,然后再拷贝给aa2

如果不行让它发生类型转换,就加上关键字explicit

class A
{
public:
	explicit A(int a)
		:_a1(a)
	{

	}
	A(const A& a)
	{
		
	}
private:
	int _a1;
	int _a2;
};

static成员

实现一个类,计算程序中创建了多少个类对象

 class A
{
public:
	A(int a = 0)
	{
		
	}

	A(const A& aa)
	{
		
	}
};

void func(A a)
{}

int main()
{
	A aa1;
	A aa2(aa1);
	func(aa1);
	A aa3 = 1;
}

因为要创建类对象,就必须要调用构造函数或者拷贝构造,我们可以设定一个全局变量进行计数。

using std::cout;
using std::endl;
int count =0;
 class A
{
public:
	A(int a = 0)
	{
		count++;
	}

	A(const A& aa)
	{
		count++;
	}
};

void func(A a)
{}

int main()
{
	A aa1;
	A aa2(aa1);
	func(aa1);
	A aa3 = 1;
	cout << count << endl;
}

但是这里有一个不好的地方,count是全局变量,可以随意修改。
这里可以把count定义在类里面,并且用static修饰,表示它是静态类成员,它不属于某个类对象,它属于所有对象,它是属于整个类的。

静态成员初始化

必须在类外面。

 class A
{
public:
	A(int a = 0)
	{
		count++;
	}

	A(const A& aa)
	{
		count++;
	}
private:
	static int count;//不属于某个对象,所于所有对象,属于整个类
					 // 声明
};
int A::count=0; // 初始化

访问静态成员

 class A
{
public:
	A(int a = 0)
	{
		count++;
	}

	A(const A& aa)
	{
		count++;
	}
	int GetCount()
	{
		return count;
	}
private:
	static int count;//不属于某个对象,所于所有对象,属于整个类
					 // 声明
};
int A::count=0; // 初始化
int main()
{
	A aa1;
	cout  << aa1.GetCount() << endl;
	return 0;
}

静态成员函数

为了调用GetCount 这个函数而特意创建一个类,这也太挫了吧,static也可以修饰函数,表示静态成员函数。

 class A
{
public:
	A(int a = 0)
	{
		count++;
	}

	A(const A& aa)
	{
		count++;
	}
	static int GetCount()
	{
		return count;
	}
private:
	static int count;//不属于某个对象,所于所有对象,属于整个类
					 // 声明
};
int A::count=0; // 初始化
int main()
{
	A aa2;
	A aa3;
	cout  << A::GetCount() << endl;
	cout << aa2.count << endl;
	cout << aa3.count << endl;
	return 0;
}

静态成员函数 – 没有this指针
这也就意味着,静态成员函数不能访问非静态成员变量。

问题

A aa4[10]调用了多少次构造函数?

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

优化情况

void func1(A aa)
{
}
void func2(const A& aa)
{
}
int main()
{
	A aa1 = 1; // 构造+拷贝构造 -》 优化为直接构造
	func1(aa1); // 无优化
	func1(2); // 构造+拷贝构造 -》 优化为直接构造
	func1(A(3)); // 构造+拷贝构造 -》 优化为直接构造

	cout << "----------------------------------" << endl;

	func2(aa1);  // 无优化
	func2(2);    // 无优化
	func2(A(3)); // 无优化


	return 0;
}

判断下面哪种方式更加高效?
类和对象(下)_第3张图片
类和对象(下)_第4张图片
直接看结果
第一种
类和对象(下)_第5张图片
第二种
类和对象(下)_第6张图片
显而易见,第二种多了一次调用构造函数和赋值运算符重载。

总结
类和对象(下)_第7张图片

为什么函数返回对象时尽量返回匿名对象我就不说了,因为我的vs2022编译器它自己帮我优化了。

const

1.举个例子

A& ref=1;//报错
const A& ref=1;//编译通过

为什么第一个代码报错,而第二个代码没有报错?
首先还是回到之前讲的这里会发生类型转换,而类型转换的时候产生了临时对象,而临时对象具有常性,所以必须用const接受。

2.匿名对象

void Func4( const A& a)
{

}
int main()
{
	A aa2;
	aa2 = Func3();
	Func4(A(1));
	return 0;
}

不用const接收参数会报错,因为匿名对象也具有常性。

所以尽量使用const&传参

你可能感兴趣的:(c++,开发语言,c++)