首页 > 编程语言 >C++入门 初始化列表 & 隐式类型转换

C++入门 初始化列表 & 隐式类型转换

时间:2024-06-08 10:04:53浏览次数:23  
标签:类型转换 初始化 int 成员 C++ 列表 scount 隐式 构造函数

目录

初始化列表

构造函数体赋值

初始化列表格式

初始化列表特性

每个成员变量在初始化列表中只能出现一次

类中以下成员必须初始化

尽量使用初始化列表初始化

数组初始化 

声明次序就是初始化顺序

多参数初始化列表

再谈隐式类型转换

拷贝

引用

explicit关键字

定义

用法

缺省值的多种方式

static成员

概念

特性


初始化列表

构造函数体赋值

构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

初始化列表格式

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

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;   
};

我们实现用两个栈实现队列该怎么写类呢?

class myqueue
{
public:
    // 初始化列表:本质可以理解为每个对象中成员定义的地方
	myqueue(int n, int& rr)
        // stack不具备默认构造,myqueue也无法生成默认构造
		:_pushst(n)
		,_popst(n)
	{
		_size = 0;
	}
private:
	// 声明
	Stack _pushst;
	Stack _popst;
	int _size;
};

初始化列表特性

每个成员变量在初始化列表中只能出现一次

每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

类中以下成员必须初始化

所有的成员,可以在初始化列表初始化,也可以在函数体内初始化(即构造函数),以下三个必须初始化:

1、引用 2、const 3、没有默认构造自定义类型成员(必须显示传参调构造)

class myq
{
public:
	myq(int n, int& rr)
		,_x(1)
		,_ref(rr)    //_ref是rr的别名
	{
		//_x = 1;
	}
private:
	// 必须在定义时初始化
	const int _x = 2;

	// 必须在定义时初始化
	int& _ref;
};

 _x在类成员变量里给了缺省值 2 ,当我们初始化_x时,初始化列表给_x初始化的值优先于类成员变量给的补丁初始化,如果初始化列表没有给值初始化,就调用补丁里的值。

尽量使用初始化列表初始化

记住以下几点:

  1. 初始化列表,不管你写不写,每个成员变量都会先走一遍。
  2. 自定义类型的成员会调用默认构造(没有默认构造就编译报错)
  3. 内置类型有缺省值用缺省值,没有的话,不确定,要看编译器,有的编译器会处理,有的不会处理。
  4. 先走初始化列表 , 再走函数体。
  5. 实践中:尽可能使用初始化列表初始化,不方便再使用函数体初始化。
数组初始化 
class arrayy
{
public:
	arrayy()    //开辟一个存放十个int的空间
		: _arr((int*)malloc(sizeof(int) * 10))
	{
		memset(_arr, 0, sizeof(int)*10);  //10个元素置0
	}
private:
	int* _arr;
};

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

声明次序就是初始化顺序

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关。如以下代码所示:

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();

	return 0;
}

//输出1 1  还是输出1 随机值  ?

答案是输出1  随机值,原因:在声明时(private)_a2在_a1前面,初始化列表先给_a2初始化,_a1赋值给_a2,但是_a1是随机值,_a2接收了一个随机值,然后又用1给_a1进行初始化。

多参数初始化列表

class A
{
public:
	//单参数构造函数	
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	//多参数构造函数
	A(int a1, int a2)
		:_a(0)
		,_a1(a1)
		,_a2(a2)
	{}
private:
	int _a;
	int _a1;
	int _a2;
};

int main()
{	
	//A aaa1(1, 2);	  //此处能通过是因为这里是逗号表达式,返回2调用了单参数构造函数	
	
	A aaa2 = { 1, 2 };

	const A& aaa3 = { 1, 2 };  //产生隐式类型转换
    //上面aaa2,aaa3的等号可以省略,但不建议
	return 0;
}

 注意多参数初始化列表调用时使用的是{ }

再谈隐式类型转换

拷贝

先看以下代码:

class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};

int main() 
{
	A aa1(1);

	// 拷贝构造
	A aa2 = aa1;

	// 隐式类型转换  (内置类型转换为自定义类型)
	A aa3 = 3;

	return 0;
}

aa1拷贝构造给aa2可以理解,为什么3(int类型)可以给aa3(自定义类型)拷贝构造呢?

 由上图可知,编译器会先用3构造一个A类型的临时变量,然后用这个临时变量拷贝构造给aa3,但是深入发现:编译器遇到连续构造+拷贝构造->优化为直接构造,如aa3。

引用

class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};

int main() 
{
	A& ra = 3;        //报错

	const A& ra = 3;  //通过

	return 0;
}

为什么第一句无法实现,而加上const就可以了呢?下图解释: 

 这里和上面的拷贝构造一样,优化为直接拷贝,注意加上const。

explicit关键字

定义

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。

 即用explicit修饰构造函数,将会禁止构造函数的隐式转换。

用法

class A
{
public:
	explicit A(int a)
		:_a(a)
	{}
private:
	int _a;
};

int main() 
{
	const A& ra = 3;    //报错

	return 0;
}

这时候3不能构造给临时对象,无法进行隐式类型转换。 

缺省值的多种方式

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	// 其他方法...
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

class A
{
public:
	//单参数构造函数	
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	//多参数构造函数
	A(int a1, int a2)	
		:_a(0)		//可以给变量,也可以给常量
		,_a1(a1)	//一般建议给常量
		,_a2(a2)
	{}
private:
	int _a;
	int _a1;
	int _a2;
};

class BB
{
public:
	BB()
	{

	}
private:
	// 声明给缺省值
	int _b1 = 1;
	int* _ptr = (int*)malloc(40);  //开辟一个40字节的空间
	Stack _pushst = 10;    //capacity初始化为10
	A _a1 = 1;		  //单参数构造函数
	A _a2 = { 1,2 };  //多参数构造函数
	A _a3 = _a2;      
};

int main()
{
	BB bb;
	return 0;
}

方式在成员变量那里展示!

static成员

概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

class A
{
public:
	A() //构造函数
	{ 
		++_scount;
	}

	A(const A & t) //拷贝构造函数
	{ 
		//GetCount();

		++_scount;
	}

	~A()//析构函数
	{ 
		//--_scount;
	}
    
	static int _scount;

private:
	// 声明
	int _a1 = 1;
	int _a2 = 1;
};

// 定义
int A::_scount = 0;

为什么_scount在哪里都可以呢?原因是他存放在静态区,不存在对象中,并且不能给缺省值,因为缺省值是给初始化列表,他在静态区不在对象中,不走初始化列表。他属于所有整个类,属于所有对象。

特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问(类静态成员必须在public中)
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
int main()
{
	A aa1;
	cout << sizeof(aa1) << endl;    //输出8  
    //因为_scount在静态区,不占aa1的空间
    
	//aa1._scount++;
	//cout << A::_scount << endl;
    //以上语句只能静态成员在public中才能实现

	return 0;
}

 那静态成员变量受private限制应该怎么访问呢?以下代码可以实现:

class A
{
public:
	A() //构造函数
	{ 
		++_scount;
	}

	A(const A & t) //拷贝构造函数
	{ 
		//GetCount();

		++_scount;
	}

	~A()//析构函数
	{ 
		//--_scount;
	}

	// 没有this指针,只能访问静态成员
	static int GetCount()
	{
		//_a1 = 1;  //无法访问
		return _scount;
	}
	
private:
	// 声明
	int _a1 = 1;
	int _a2 = 1;

	static int _scount;
};

// 定义
int A::_scount = 0;

这里的静态成员函数没有this指针,只能访问静态成员变量,我们要静态成员变量有什么用呢?

A func()
{
	A aa4;    //构造  //_scount = 4

	//返回值调用了一次拷贝构造,原因是传值调用
    //vs2019会+1,vs2022不会+1
	return aa4;		
}

int main()
{
	A aa1;    //构造  //_scount = 1
	A aa2;    //构造  //_scount = 2
	A aa3(aa1); //拷贝构造  //_scount = 3

	func();  //_scount = 4

	cout << A::GetCount() << endl;  //输出4

	return 0;
}

联系上A类里的成员函数和成员变量,我们通过静态成员变量记录拷贝构造和构造的次数,main函数里共构造了4个变量,所以输出为4。

标签:类型转换,初始化,int,成员,C++,列表,scount,隐式,构造函数
From: https://blog.csdn.net/fen_0108/article/details/139401819

相关文章

  • 【C++练级之路】【Lv.23】C++11——可变参数模板、lambda表达式和函数包装器
    快乐的流畅:个人主页个人专栏:《算法神殿》《数据结构世界》《进击的C++》远方有一堆篝火,在为久候之人燃烧!文章目录一、可变参数模板1.1参数包的概念1.2参数包的展开1.3emplace系列二、lambda表达式2.1lambda的格式2.2捕捉列表2.3lambda的原理2.4......
  • 类和对象(二)(C++)
    初始化列表classDate{public:Date(intyear,intmonth,intday){_year=year;_month=month;_day=day;}private:int_year;int_month;int_day;};虽然上述构造函数调用之后,对象中已经有了一个初......
  • 07c/c++面向对象
    07C/c++零碎语法目录文章目录07C/c++零碎语法C1.封装2.继承3.多态C++4.1封装4.1.1封装的意义5.继承5.1继承的基本语法5.2继承方式5.3继承中的对象模型5.4继承中构造和析构顺序5.5继承同名成员处理方式5.6继承同名静态成员处理方式5.7菱形继承C1.封......
  • c++类与对象
    classBook{public:charname;intpages;voidprintpages();}voidprintpages(){std::cout<<"书的页数"<<endl;}在定义类的属性和方法时可以选择1.public:公有成员(在程序中类的外部是可访问的)。2.protected:受保护成员(在类的外部是不可访问的,甚......
  • c++的面对对象 的 虚函数
    #include<iostream>#include<string>classAnimal{public:virtualvoidmakeSound()=0;//纯虚函数,所有的动物都应该提供自己的叫声};classDog:publicAnimal{public:voidmakeSound()override{std::cout<<"Woof!"<&l......
  • 【C++修行之道】类和对象(五)日期类的实现、const成员、取地址及const和取地址操作符重
    目录一、日期类的实现Date.h 1.1GetMonthDay函数(获取某年某月的天数) 问:这个函数为什么不和其他的函数一样放在Date.cpp文件中实现呢?1.2CheckDate函数(检查日期有效性)、Print函数(打印日期)1.3实现日期类的逻辑运算符重载<运算符的重载 ==运算符重载其他运算符重载......
  • C++AB类相互包含
    本文讨论我们该如何实现如下结构,而不会报错:classA{Bb;}classB{Aa;}我们知道编译器的执行顺序是从上到下的,当我们在A中使用B时,B还没有定义,甚至没有声明,所以在这里我们先给代码加上一段前向声明。classB;classA{Bb;}前向声明让编译器知道B类是......
  • 一篇文章带你搞懂C++引用(建议收藏)
    引用6.1引用概念引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"typedef是给类型取别名引用是给变量取别名注意:引用类型必须和引用实体是同种类......
  • 最大公约数(gcd())和最小公倍数(lcm())的c语言和c++详细解法
    最大公约数(gcd())和最小公倍数(lcm())最大公约数:定义:两个或多个整数共有的约数中最大的一个。例如:整数12和18,他们的公约数有1、2、3、6,其中最大的公约数是6。c语言解法:辗转相除法和更相减损法1、辗转相除法:思路:先求解较大的数除以较小的数的余数,再用较小的数除以前......
  • C++STL---list模拟实现
    本文我们模拟实现STL中的list,为了模拟实现list,实际上我们需要实现三个类,分别为:_list_node,_list_iterator,list。我们先看一下这三个类的基本组成,主要是看看每个类中包含的变量有什么:namespaceCYF{ //模拟实现list当中的结点类 template<classT> struct_list_node......