Day4:构造函数和析构函数

本文详细介绍了C++中的构造函数、析构函数、拷贝构造、深浅拷贝以及对象初始化等概念。重点讲解了构造函数的默认、重载与委托,析构函数的自动调用,拷贝构造函数的使用场景,以及深拷贝与浅拷贝的区别。此外,还探讨了对象生命周期、初始化列表和成员初始化的重要性。
摘要由CSDN通过智能技术生成

构造函数 

构造函数无返回值,函数名需要和类名相同才行。

默认的构造函数无参数。

构造函数的作用,和构造函数的重载(与普通函数的重载不同,需要进行赋初值才行)

delete删除默认的构造函数数。(一旦自己构造后,默认的构造函数就没了)

gf()=delete;

用gf()=default;听说运行更快(这才是默认初始构造函数)

#include<iostream>

using namespace std;


class gf
{
public:
	//gf(string mname="abc", int mage=12)
	//{
	//	name = mname;
	//	age = mage;
	//}
	合理利用函数的缺省,等效与下面的代码
	gf();
	gf(string mname);
	gf(string mname, int mage);
protected:
	string name;
	int age;
private:
};
int main()
{
	gf kk;
	gf kk2("dad", 21);
	gf kk3("nda", 13);
	return 0;
}

初始化参数列表的写法:(灵活)(在构造函数的时候写)

(也就是在类中用)(所以也可以类中声明,类外实现)

(好处:自动识别形参和实参,名字可以起相同的)

(不一定所有的东西都要在初始化参数列表中实现,也可以同时在数据成员中实现)

string myname ="me";//
class pig
{
public:
	pig(string mname, int mage) : name(mname), age(mage)
	{
		cout << "初始化参数列表" << endl;
		//继承和类的组合必须采用初始化参数列表的写法
	}
	pig(int bage):name(myname),age(bage){}
	//这样写也是可以的,从全局区拿一个变量给他赋值 (灵活)
protected:
	string name;
	int age;
};

委托构造:(一个构造函数调用另一个构造函数)

作用:这样就不需要自己在手动去初始化(相当于给了个初值),也可以自己去

class cat 
{
public:
	cat(string name,int age):name(name),age(age){}
	cat() :cat("小猫咪", 5) {}//自己不做初始化
	//委托构造(C11之后才有的)
protected:
	string name;
	int age;
};

析构函数 

析构函数有默认、无返回值、无参数->不能被重载

 会自己在生命周期结束的时候自行调用

一般写到指针的时候,会需要自己手写一个(生命周期结束是自动调用释放内存的函数)。

class giegie
{
public:
	giegie(const char*str,int age):age(age)
	{
		char* name = new char[strlen(name) + 1];
		strcpy_s(name, strlen(name) + 1, str);
	}
	~giegie();
protected:
	char* name;
	int age;
};
//可以在类外实现类中的析构函数
giegie::~giegie()
{
	cout << "Im 析构函数" << endl;
	delete[]name;
	name = nullptr;
}
int main()
{
	{
		giegie wbm("wbm",18);
	}//出了这个大括号,wbm生命周期结束,自行调用析构收尾
	return 0;
}

一般不会手动调用(手动调用对象也不会“死”),因为你手动调用后再加上系统自行调用,会二次释放内存,引发中断报错。 

特别的,在new一个对象的时候需要加上delete才会调用析构函数。

int main()
{
	giegie* czr = new giegie{ "chenzongran",19 };
	//delete czr;
	czr = nullptr;
	return 0;
}

此处不用delete则不调用析构函数。 

加上后

 注意:补充说明下delete的用法:delete[] p可以写成delete p但反过来不行。

这里在new一个对象的时候,由于不是对象的数组,所以应该写成delete p而不是delete p[];

拷贝构造函数

①和构造函数写法相同,只不过操作对象是固定的(也就是对对象的引用),所以我们可以用一个对象给另一个对象初始化。

②特别的:匿名函数给人家初始化的话,需要加上const修饰。(相当于是一个右值了,常属性)

	giegie(giegie& wbm)//wbm是一个参数
	{
		age = wbm.age;
		name = wbm.name;
	}//构造出来的一个对象由括号里面的对象进行初始化

那么我们在主程序就可以这样写,来初始化一个成员对象

	giegie gzj("郭子杰", 19);
	giegie fangcao(gzj);

拿已经创建好的gzj来初始化fangcao

以上为一种显式调用

隐式调用

	giegie gzj("郭子杰", 19);
	giegie fangcao=gzj;

区分一下运算符重载:(先创建对象,在进行赋值)

giegie gzj("郭子杰", 19);
	giegie fangcao;
	fangcao = gzj;

匿名函数  (单独一个匿名对象,创建即死,应转交所有权,才不会调用析构函数而死亡)

(即无名)

class peg
{
public:
	peg(string name, int age) :name(name), age(age) {};
	peg(const peg& kk);//匿名构造一定要加上const
protected:
	int age;
	string name;
private:

};
int main()
{
	peg wbm=peg("匿名",130);
	return 0;
}

 函数两种传参方式:

①不传引用:(效率低,还会再次创建一个对象)

②传引用:效率高,取了个别名!!

class peg
{
public:
	peg(string name, int age) :name(name), age(age) {};
	peg(const peg& kk);//匿名构造一定要加上const
	void print()
	{
		cout << name << "\t" << age << endl;
	}
protected:
	int age;
	string name;
private:

};
void getdata1(peg wbm)//wbm是形参
{
	wbm.print();
}
void getdata2(peg& wbm)
	{
	wbm.print();
	}

两个getdata函数  第二种是更好的。

 深浅拷贝

①默认浅拷贝,但当有指针的时候,会导致拷贝的时候,指针指向同一块地址,导致析构问题(重复释放内存)。

正常用的都是浅拷贝。

深拷贝:(需要自己写拷贝函数,加上new和拷贝赋值的操作)->可以方便的解决浅拷贝问题

区别于仅改变指针的指向(应该是将值拷到新的内存里)->给一块新的内存,避免重复free

class peg
{
public:
	peg(const char* mname, int age) :age(age)
	{
		name = new char[strlen(mname) + 1];
		strcpy_s(name, strlen(mname) + 1, mname);
	}
	peg(const peg& kk);//匿名构造一定要加上const
	peg(peg& kkk)
	{
		name=new char[strlen(kkk.name) + 1];
		strcpy_s(name, strlen(kkk.name) + 1, kkk.name);
		age = kkk.age;
	}
	~peg()
	{
		cout <<"我是普通析构函数" << endl;
	}
	~peg()
	{
		cout << "我是特殊析构函数" << endl;
		delete[] name;
	}
protected:
	int age;
    char* name;
private:
};

这样就能有效解决了啦。 

构造与析构顺序问题:

  • 普通对象,构造顺序和析构顺序是相反

  • new出来的对象,delete会直接调用析构函数

  • static对象,当程序关闭的时候,生命周期才结束,所以是最后释放

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<string>
    
    using namespace std;
    class testOrder
    {
    public:
    	testOrder(string name, int age) :name(name), age(age) 
    	{
    		cout << name;
    	}
    	~testOrder()
    	{
    		cout << name;
    	}	
    protected:
    	int age;
    	string name;
    private:
    
    };
    
    int main()
    {
    	testOrder a("A", 21);
    	testOrder b("B", 11);
    	testOrder* p = new testOrder("K", 123);
    	static testOrder kk("X", 2);
    	delete p;
    	p = nullptr;//ABKXKBAX
    }

    C++结构体

  • ①权限默认是公有

  • ②类中有的,它也有,当析构构造一旦写了,就得当类来使用,包括初始化成员的使用。

  • ps:data()和c_str()的返回值是char*类型。

好问题:初始化成员累表是跟声明(在类里面)还是跟定义(放在类外)??

答:跟定义。如以下代码:声明可以不写初始化列表,只要在定义的时候写就行。(实际上也是函数体内的东西)

using namespace std;
class testOrder
{
public:
	testOrder(string name, int age);
	~testOrder()
	{
		cout << name;
	}
protected:
	int age;
	string name;
private:

};
testOrder::testOrder(string name, int age) : name(name), age(age)//相当于是函数内的东西
{
	cout << name << endl;
}
int main()
{
	testOrder kk("abc", 12);
}

写一个MyString类型:

有几个感触比较深的

①在vs2022中拷贝构造,用已创建好的对象构造新对象,需要在拷贝构造函数的参数里加上const修饰才行,否则会显示没有适合的拷贝构造函数。

②写代码的时候要细心,到底是给谁开辟空间,一开始给对象temp开辟空间了,应该是给temp的string_name开辟空间。

③用data()也就是函数名来代表一个数据成员,只需要return他就可以了

#include<iostream>
#include<string>
#include<cstring>

using namespace std;
class MyString
{
public:
	int strSize;
	//此处有指针,下面也需要有拷贝函数,那么我们应该采用深拷贝的方式,避免重复释放内存
	MyString(const char* name=" ")
	{
		strSize = strlen(name) + 1;
		string_name = new char[strSize];
		strcpy_s(string_name, strSize, name);
	}
	MyString(const MyString& mm)
	{
		strSize = mm.strSize;
		string_name = new char[strSize];
		strcpy_s(string_name, strSize, mm.string_name);
	}
	//data()和c_str()
	char* data()
	{
		return string_name;
	}
	char* c_str()
	{
		return string_name;
	}
	//append
	MyString append(const MyString& wbm)//append函数
	{
		MyString temp;
		temp.strSize = wbm.strSize + strSize - 1;//减去一string自带\0 这里包含了两个 所以可以减去一个
		temp.string_name = new char[temp.strSize];
		memset(temp.string_name, 0, temp.strSize);
		strcat_s(temp.string_name, temp.strSize, string_name);
		strcat_s(temp.string_name, temp.strSize, wbm.string_name);
		return temp;
	}
	int compare(const MyString& mm)
	{
		return strcmp(string_name, mm.string_name);
	}
	~MyString()
	{
		delete[]string_name;
		string_name = nullptr;
	}
protected:
	char* string_name;
};
int main()
{
	//功能一:
	MyString str1("Das");
	MyString str2("kasd");
	MyString str3 = "KASD";
	MyString str4(str1);
	//功能二
	cout << str2.data() << endl;
	cout << str2.c_str() << endl;
	//功能三append 连接功能
	MyString strOne = "one";
	MyString strTwo = "two";
	MyString strThree=strOne.append(strTwo);
	cout << strThree.data() << endl;	    //onetwo
	//功能四 compare函数
	cout << strOne.compare(strOne) << endl;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个可以满足要求的 People 类的示例: ``` class Date { private: int year, month, day; public: // 构造函数析构函数 Date(int y, int m, int d): year(y), month(m), day(d) {} ~Date() {} // 拷贝构造函数 Date(const Date& other): year(other.year), month(other.month), day(other.day) {} // 内联成员函数 inline int getYear() const { return year; } inline int getMonth() const { return month; } inline int getDay() const { return day; } // 带缺省形参值的成员函数 void setYear(int y = 1970) { year = y; } void setMonth(int m = 1) { month = m; } void setDay(int d = 1) { day = d; } }; class People { private: int number; char sex; Date birthday; string id; public: // 构造函数析构函数 People(int n, char s, const Date& b, const string& i): number(n), sex(s), birthday(b), id(i) {} ~People() {} // 拷贝构造函数 People(const People& other): number(other.number), sex(other.sex), birthday(other.birthday), id(other.id) {} // 内联成员函数 inline int getNumber() const { return number; } inline char getSex() const { return sex; } inline const Date& getBirthday() const { return birthday; } inline const string& getId() const { return id; } // 带缺省形参值的成员函数 void setNumber(int n = 0) { number = n; } void setSex(char s = 'M') { sex = s; } void setBirthday(const Date& b = Date(1970, 1, 1)) { birthday = b; } void setId(const string& i = "") { id = i; } }; ``` 以上的示例中,包括了构造函数析构函数、拷贝构造函数、内联成员函数以及带缺省形参值的成员函数。 构造函数析构函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_Ocean__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值