c++~第3课-----构造函数

目录

构造函数概念:

构造有参,无参,重载函数

初始化列表

析构函数:

拷贝构造函数

深浅拷贝

构造和析构顺序问题

c++结构体:


构造函数概念:

1、构造函数概念
一个类的对象被创建的时候,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作。因此,构造函数的核心作用就是,初始化对象的数据成员

  • 构造函数长什么样的?

 函数名和类名相同

 没有返回值(连void也没有)

如果不写构造函数,任何类中都存在一个默认的构造函数

  • 默认的构造函数是无参的。

构造函数在构造对象的时候调用

delete可以删掉默认的函数

指定使用默认的无参构造函数,用default说明

允许构造函数调用另一个构造函数,只是要用初始化参数列表的写法

初始化参数列表:只有构造函数有:构造函数(参数1,参数2...):成员1(参数1),成员2(参数2),...{}

作用:避免形参名和数据成员名相同导致的问题

当我们自己写了构造函数,默认的构造函数就不存在

  • 构造函数干嘛的?

构造函数用来构造对象

构造函数更多是用来初始化数据成员

  • 构造函数重载为了什么?

为了构造不同长相的对象

class MM
{
public:
	MM() = delete;//删除默认构造函数
	void print()
	{
		cout << name << age << endl;
	}
protected:
	string name;
	int age;

};
int main()
{
	MM mm;//E1790	无法引用 "MM" 的默认构造函数 -- 它是已删除的函数


	return 0;
}

构造有参,无参,重载函数

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class MM
{
public:
	//MM() = delete;//删掉默认构造函数
	void print()
	{
		cout << name << age << endl;
	}
	//有参构造函数
	MM(string mmName, int mmAge)
	{
		name = mmName;
		age = mmAge;
		cout << mmName << mmAge << endl;
	}
	//无参构造函数
	MM()
	{
		cout << "无参函数" << endl;
	}
	//MM() = default;//使用的是默认无参构造函数
protected:
	string name;
	int age=1;

};
//为了能够构造不同长相的对象,我们会给构造函数缺省处理
class boy
{
public:
	//如果出错:没有与之匹配的构造函数  原因:没构造函数
	/*boy(string Bname=" ", int Bage=20)
	{
		name = Bname;
		age = Bage;
		cout << Bname << "\t" << Bage << endl;
	}*/
	//上面函数等效下面三个函数功能
	boy(){}
	boy(string Bname) { name = Bname; }
	boy(string Bname, int Bage) { name = Bname; age = Bage; }
	void print()
	{
		cout << name << "\t" << age << endl;
	}
protected:
	string name="默认值";
	int age=11;

};
int main()
{
	//构造无参的对象,需要无参构造函数
	//MM mm;//E1790	无法引用 "MM" 的默认构造函数 -- 它是已删除的函数
	MM mm("大聪明", 22);
	MM m2;
	boy b1;
	b1.print();
	boy b2("熊出没");
	b2.print();
	boy b3("小狗", 66);
	b3.print();
	return 0;
}

初始化列表

在定义带参构造函数时,可以两种写法,都可以进行传参。
(1)正常写法(2)初始化列表写法

class boy
{
public:
	//初始化参数列表
	boy(string Bname, int Bage) :name(Bname), age(Bage)//不一定是源自于左边的参数,也可以是外部的参数类型一样即可
	{
		cout <<"初始化参数列表" << endl;
		//继承和类的组合必须采用初始化参数列表写法
	}
	//正常写法
	boy(string Bname, int Bage)
	{
		name = Bname;
		age = Bage;
		cout << "正常" << endl;

	}

构造函数调用另一个构造函数初始化数据

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class boy
{
public:
	//初始化参数列表
	boy(string Bname, int Bage) :name(Bname), age(Bage)//不一定是源自于左边的参数,也可以是外部的参数类型一样即可
	{
		cout <<"初始化参数列表" << endl;
		//继承和类的组合必须采用初始化参数列表写法
	}
	//正常写法
	boy(string Bname, int Bage)
	{
		name = Bname;
		age = Bage;
		cout << "正常" << endl;

	}
	void print()
	{
		cout << name << "\t" << age << endl;
	}
protected:
	string name="默认值";
	int age=11;
};
//构造函数蓝羽调用另一个构造函数初始化数据
class TT
{
public:
	TT(string name, int age) :name(name), age(age){}//形参实参名字可以一样系统会自动识别
	//委托构造
	TT():TT("默认",11){}
	void print()
	{
		cout << name << "\t" << age << endl;
	}
protected:
	string name;
	int age;
};

析构函数:

  • 析构函数长什么样子?

①无返回值②无参数③函数名:~类名④不写的话会存在默认的析构函数⑤析构函数不需要直接调用,对象死亡之前会调用析构函数

  • 析构函数用来干嘛?

什么时候需要自己手动写析构函数;当类中的数据成员是指针,并且动态申请类内存就需要手动写析构;析构函数用来释放数据成员申请动态内存(一个对象出生的时候,使用构造函数,死掉的时候,使用析构函数。

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class MM
{
public:
	MM(const char* pstr, int age) :age(age)
	{
		str = new char[strlen(pstr) + 1];
		strcpy(str, pstr);
	}
	void print()
	{
		cout << str << "\t" << age << endl;
	}
	~MM();
protected:
	char* str;
	int age;
};
MM::~MM()
{
	cout << "我是析构函数" << endl;
	delete[] str;
}
int main()
{
	{
		MM mm("张三", 111);
		mm.print();
		//mm.~MM();//可以手动调但是会引发二次释放问题引发中断
	}//出作用域前调用析构函数
	cout << "主函数" << endl;
	//new一个对象的时候,只有delete 才会调用析构函数
	{
		MM* pp = new MM("你好", 99);
		pp->print();
		delete pp;
		pp = nullptr;
	}

	return 0;
}

拷贝构造函数

  • 拷贝构造函数也是构造函数,长相和构造函数一样的,只是参数是固定

拷贝构造函数唯一的参数是对对象引用

  • 不写拷贝构造函数,也存在一个默认的拷贝构造函数
  • 拷贝构造函数作用:通过一个对象去初始化另一个对象
  • 问题:

什么时候调用?

答:当通过一个对象去创建出来类一个新的对象时候需要调用拷贝。

拷贝构造什么时候需要加const修饰参数?

答:当存在匿名对象赋值操作的时候,必须要const修饰。

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class MM
{
public:
	MM(string name, int age) :name(name), age(age)
	{
		cout << "拷贝构造" << endl;
	}
	void print()
	{
		cout << name << "\t" << age << endl;
	}
	//拷贝构造
	MM(const MM& mm)//相当于MM girl(mm);
	{
		name = mm.name;//相当于girl.name=mm.name;
		age = mm.age;//相当于girl.age=mm.age;
		cout << "拷贝构造" << endl;
	}
	MM(){}
	//MM(MM&& mm)
	//{
	//	name = mm.name;//相当于girl.name=mm.name;
	//	age = mm.age;//相当于girl.age=mm.age;
	//	cout << "右值引用" << endl;
	//}
	
protected:
	string name;
	int age;

};
//第一种调用形态
void printData(MM mm)//MM mm=实参
{
	//调用拷贝构造
	mm.print();
}
void printData2(MM& mm)//不存在拷贝本(这里形参就是实参),不会调用拷贝构造
{
	mm.print();
}

int main()
{
	MM mm("效果", 11);
	mm.print();
	//默认拷贝构造函数
	//显示调用
	MM girl(mm);//通过一个对象创建另一个对象
	girl.print();
	cout << "显示调用" << endl;
	//隐式调用
	MM girl2 = mm;//拷贝构造
	girl2.print();
	cout << "隐式调用" << endl << endl;;
	//运算符重载,需要建个无参默认函数,不然会报错(运算符重载不会调用拷贝构造)
	MM girl3;
	girl3 = mm;
	girl3.print();
	//函数传参
	cout << endl;
	cout << "第一种调用形态" << endl;
	printData(mm);
	cout << "第二种调用形态" << endl;
	printData2(mm);

	//无名对象(匿名对象),在创建对象时,拷贝构造移动要加const修饰
	//MM temp = MM("匿名", 11);//不能调用拷贝构造,但是可以加const就可以
	cout << endl;
	MM temp;
	temp = MM("匿名", 11);
	temp.print();

	return 0;
}

深浅拷贝

值是没深浅拷贝的说法,只有指针有影响。

浅拷贝:默认的拷贝构造叫做浅拷贝

深拷贝:拷贝构造函数中做了new内存操作,并且做了拷贝赋值操作(把值拷贝到新的内存里)

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class MM
{
public:
	MM(const char* mname, int age):age(age)
	{
		name = new char[strlen(mname) + 1];
		strcpy(name, mname);
	}
	MM(const MM& object)
	{
		//name = object.name;
		name = new char[strlen(object.name) + 1];
		strcpy(name,object.name);
		//name = object.name;//屏蔽上面这一句只new一个内存也不叫深拷贝,还是浅拷贝
		age = object.age;
	}
	~MM()
	{
		delete[] name;
	}
	void print()
	{
		cout << name << "\t" << age << endl;
	}
protected:
	char* name;
	int age;

};
int main()
{
	//浅拷贝
	MM mm("熊出没", 444);//一起运行会崩溃因为调用了三次析构
	MM girl(mm);
	MM girl2 = mm;

	return 0;
}

构造和析构顺序问题

  • 普通对象,构造顺序和析构顺序是相反的。
  • new出来的对象,delete会直接调用析构函数
  • static对象,当程序关闭的时候,生命周期才结束,所以是最后释放
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class MM
{
public:
	MM(string name = "x"):name(name)
	{
		cout << name;
	}
	~MM()
	{
		cout << name;
	}
protected:
	string name;
};
int main()
{
	{
		MM mm1("A");//A
		static MM mm2("B");//B,是程序结束生命周期才结束
		MM* p = new MM("C");//C
		MM mm4[4];//xxx
		delete p;//C,delete是直接调用析构的
	}
	

	return 0;
}

c++结构体:

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
struct MM
{
	//结构体默认公有属性
	//类中默认私有
//protected:
	string name;
	int age;

	MM(string name) :name(name)
	{
		cout << "构造函数" << endl;
	}

	MM(const MM& object)
	{
		name = object.name;
		age = object.age;
		cout << "拷贝函数" << endl;
	}
};
int main()
{
	//采用创建时候赋值方式,也是调用构造函数
	//MM object = { "你好",11};  //错误,因为没有两个参数的构造函数
	MM object = { "你好" };
	cout << object.name << "\t" << object.age << endl;
	//c++结构体一旦写了构造函数,就必须按照c++类的方式去用
	MM mm(object);

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luckys-Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值