C++3--构造函数、冒号语法

2023-05-16

一、构造函数

1、为什么要有这个概念:

例如下面的代码,对于Table类,可以通过t.Set公有的方法给对象设置内容,但是如果每次创建对象都调用该方法设置信息,就会有点麻烦,那能否在对象创建时,就将信息设置进去?

而我们的需求是不通过对象去调用初始化对象的数据,我们希望当这个对象创建出来的时候,他就已经是具有一定的初始值的,

为解决完成对象的初始化问题,引出构造函数概念

class Table
{
public:
	void Set();
	void Print();
private:
	int m_length;
	int m_width;
	int m_height;
};

void Table::Set()//设置长宽高
{
	m_length = 120;
	m_width = 40;
	m_height = 80;
}

void Table::Print()//打印长宽高
{
	cout<<m_length<<" "<<m_width<<" "<<m_height<<endl;
}

void main()
{
	Table t;//此代码一运行,对象t就已经被定义,但其属性没有确定,就存在一定问题
	//t.Set();
	t.Print();//没有运行Set,输出结果为随机值
}

运行结果:
在这里插入图片描述

2、构造函数

定义:

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。

特性:

特殊的成员函数,函数名和类名相同,无返回类型,可以带参数(意味着可以重载);

函数名与类名相同。
无返回值。
编译器自动调用对应的构造函数。
构造函数可以重载。

功能:

构造函数的功能是用来完成对象的初始化的,需要注意的一点是,虽然构造函数叫"构造"函数,但是构造函数并不是用来构造对象的,

特点:

在定义对象的时候,自动调用当前类的构造函数;
如果程序员没有定义构造函数,则类会提供一个默认的构造函数,给类中的数据成员分配空间(栈上的空间)。

遇到对象,要自动调用当前类的构造函数,调用构造函数的步骤:(基础,后续会扩充)
1、传参;
2、根据数据成员在类中的声明顺序开辟空间;
3、执行构造函数函数体;

使用示例:

class Table
{
public:
	//如果程序员没有写,类会提供一个默认的构造函数,默认构造函数是无参数
	Table(int l = 120,int w =40,int  h= 80)
	//Table(int l,int w,int h)//会报错,要不改成无参,要不加值
	{
		m_length = l;
		m_width = w;
		m_height = h;
		cout<<"Table:"<<endl;
	}
	void Print()
	{
		cout<<m_length<<" "<<m_width<<" "<<m_height<<endl;
	}
private:
	int m_length;
	int m_width;
	int m_height;
};

int main()
{
	Table t;//定义对象 --- 自动调用Table的构造函数
	t.Print();
	Table t1(1);
	t1.Print();
	Table t2(1,1);
	t2.Print();
	Table t3(1,1,1);
	t3.Print();

	cout<<" "<<endl;

	Table tt[5];//定义了一个对象数组 tt[0] ~ tt[4],会定义5次
	cout<<"main"<<endl;

	cout<<" "<<endl;

	Table *p = &t;//p,不是对象,只是一个指向Table类型的指针,占四字节空间
	p->Print();
	//int *p;类比于int
}

运行结果:

在这里插入图片描述

3、组合(多个类组合使用):

组合:强拥有,一个类的对象作为另外一个类的数据成员,整体和部分的关系, 生存周期是一样的

类比于聚合:弱拥有,

注意以下示例代码,类的声明是根据数据成员在类中的声明顺序来定的。

示例代码:

class CPU
{
public:
	CPU()//仅仅用来表示调用声明顺序
	{
		cout<<"CPU"<<endl;
	}
};

class Mouse
{
public:
	Mouse()
	{
		cout<<"Mouse"<<endl;
	}
};

class KeyBoard
{
public:
	KeyBoard()
	{
		cout<<"KeyBoard"<<endl;
	}
};

class Computer
{
public:
	Computer()
	{
		cout<<"Computer"<<endl;
	}
private:
	CPU cpu;
	Mouse ms;
	KeyBoard kb;
};
void main()
{
	Computer c;
}

运行结果:

在这里插入图片描述

二、冒号语法

1、类成员初始化的困惑,为什么需要冒号语法?

困惑在于:

一个类的对象作为另一个对象的数据成员时,例如以下代码,定义Student对象时,定义其成员函数int m_num;char m_name[20];Date birthday;时,定义birthday时,会先调用类Date,先调用其构造函数Date,所以在定义birthday时,就已经将其初始化,开辟完空间后,已有值,给了初始化的值。
在运行到birthday = d;时,其是赋值,给其赋我想要的值,而不是初始化。所以运行Date d(2001,1,1);时,不能一次赋值所需要的值,不能在开辟空间的时候赋给所需要的值。
类似于(先初始化,在赋值):
int a = 0;//初始化
a = 10;//赋值

class Date
{
public:
	//Date(int y,int m,int d)
	Date(int y=2000,int m=12,int d=12)//得带初始值,否则报错
	{
		m_y = y;
		m_m = m;
		m_d = d;
		cout<<"Date"<<m_y<<" "<<m_m<<" "<<m_d<<endl;
	}
	void Show()
	{
		cout<<m_y<<" "<<m_m<<" "<<m_d<<endl;
	}
private:
	int m_y;
	int m_m;
	int m_d;
};
class Student
{
public:
	Student(int num,char *name,Date d)
	{
		m_num = num;
		strcpy(m_name,name);
		birthday = d;
	}
	void Print()
	{
		cout<<m_num<<" "<<m_name<<" ";
		//cout<<birthday<<endl;//error,birthday是搁对象,不能直接输出
		birthday.Show();
	}
private:
	int m_num;
	char m_name[20];
	Date birthday;//到这一步,没有办法传参
};
int main()
{
	Date d(2001,1,1);
	Student s(1001,"lisi",d);
	s.Print();
}

运行结果:
在这里插入图片描述

Date(int y,int m,int d) 的报错:

在这里插入图片描述

2、冒号语法

类成员初始化的困惑 — 冒号语法 — 成员初始化列表
int a = 10;
int a = 0;//初始化
a = 10;//赋值
所以为避免这种情况,需要在初始化的时候,不进入到构造函数函数体内。既传参后,赋值前。()后{}前
在这里插入图片描述

使用

:m_num(num) — 把num的值赋值给m_num
可以等价替换的,用()
写在:后为初始化,写在{}内为赋值

class Date
{
public:
	//可以不带默认值,如果当前的类的构造函数里面的参数没有写默认值,那么必须在另外一个类的那个构造函数的冒号语法处显示的传参
	Date(int y,int m,int d):m_y(y),m_m(m),m_d(d)
	{
		//cout<<"Date"<<m_y<<" "<<m_m<<" "<<m_d<<endl;
	}
	void Show()
	{
		cout<<m_y<<" "<<m_m<<" "<<m_d<<endl;
	}
private:
	int m_y;
	int m_m;
	int m_d;
};

class Student
{
public:
	//Student(int num,char *name,Date d):birthday(d)
	//Student(int num,char *name,Date d):birthday(y,m,d)//error,m_m不能直接赋值
	Student(int num,char *name,int y,int m,int d):m_num(num),birthday(y,m,d)
	{
		//m_num = num;
		strcpy(m_name,name);
		//birthday = d;
	}
	//Student(int num,char *name,Date d)
	//{
	//	m_num = num;
	//	strcpy(m_name,name);
	//	birthday = d;
	//}
	void Print()
	{
		cout<<m_num<<" "<<m_name<<" ";
		//cout<<birthday<<endl;//error
		birthday.Show();
	}
private:
	int m_num;
	char m_name[20];
	Date birthday;//到这一步,没有办法传参
};

int main()
{
	//Date d(2001,1,1);
	//Student s(1001,"lisi",d);//困惑在于不能一次性初始化Student类,需要先初始化其中的Date类
	Student s(1001,"lisi",2001,5,15);
	s.Print();
}

运行结果

在这里插入图片描述

3、调用构造函数的3步骤:

1、传参;
2、根据数据成员在类中的声明顺序,用冒号语法后面的值继续初始化;
3、执行构造函数函数体;

相关笔试题:

//笔试题
//根据数据成员在类中的声明顺序,用冒号语法后面的值继续初始化;
//先声明m_i,再声明m_j
class A
{
public:
	A(int i = 0,int j = 0):m_j(j),m_i(m_j)//先声明m_i,在声明m_j
	//即先运行m_i(m_j),在运行m_j(j)
	{
	}
	void print()
	{
		cout<<m_i<<" "<<m_j<<endl;
	}
private:
	int m_i;
	int m_j;
};
void main()
{
	A a(4,8);
	a.print();
}

运行结果:
在这里插入图片描述

冒号还解决了类成员是常量和引用时,不能更改的情况

如果没有冒号语法,那么数据成员不能有常量和引用

//冒号语法,解决了类成员是常量和引用时,不能更改的情况
class A
{
public:
	A(int i,int j):m_i(i),m_j(j)
	{
		//error,不能赋值
		/*m_i = i;
		m_j = j;*/
	}
private:
	//const int m_i = 7;//error,不能在这初始化
	const int m_i;//常量
	int &m_j;//引用
};

void main()
{
	//error,常量必须在声明的时候初始化
	//const int a;
	//a = 7;

	//error,引用必须在声明的时候初始化
	/*int a = 10;
	int &b;
	b=a*/;
}

笔试题:

main函数之前是否可以执行一段代码,如果可以,请举例

class A
{
public:
	A(){cout<<"A"<<endl;}
};

A a;

void main()
{
	cout<<"main"<<endl;
}

在这里插入图片描述
静态变量:
静态变量的开辟空间,是由程序员决定,只有程序员使用时才在堆里开辟空间。在第一次遇到当前对象的时候开辟空间,后面就不会在开辟空间了。



class A
{
public:
	A(){cout<<"A"<<endl;}
};

A a;

void fn()
{
	//static A b;
	static int a = 0;
	int b = 0;
	a++;
	b++;
	cout<<a<<" "<<b<<endl;
}

void main()
{
	cout<<"main"<<endl;
	/*for(int i = 0;i<5;i++)
	{
		fn();
	}*/

	for(int i = 0;i<5;i++)
		fn();
}

运行结果:

在这里插入图片描述
相关代码:

void fn()
{
	//static A b;
	static int a = 0;
	int b = 0;
	a++;
	b++;
	cout<<a<<" "<<b<<endl;
}

void main()
{
	/*for(int i = 0;i<5;i++)
	{
		fn();
	}*/
	for(int i = 0;i<5;i++)
		fn();
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++3--构造函数、冒号语法 的相关文章

随机推荐