C++------类与对象(六个默认函数)

本文详细介绍了C++中的构造函数、析构函数、拷贝构造函数、赋值运算符重载以及const成员函数的概念、特点和使用。重点讨论了如何处理内置类型和自定义类型,以及如何避免内存泄漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

  • 1. 构造函数
  • 2. 析构函数
  • 3. 拷贝构造函数
  • 4. 赋值运算符重载
  • 5. const成员函数
  • 6. 取地址及const取地址操作符重载


前言

C++支持一个类即使是空类,它也会自动生成六个默认的函数。

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。


一、构造函数

1.什么是构造函数

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

2.构造函数特点

是用来初始化类内置数据类型或者自定义数据类型,函数名和类名相同,没有返回值,它可以重载,由编译器自动调用。至于为什么,它是c++语法规定的,和C语言函数不同。

3,构造函数的类型

1.默认构造函数。

全缺省构造函数和无参的构造函数我们都可以认为是默认构造函数。

(1)如果我们构造函数没写,编译器会自己生成该类的自己的默认构造函数,它不会对内置类型进行初始化,对自定义类型初始化会调用自定义类型自己的构造函数。注意:如果已经写了构造函数,编译器就不会生成自己的默认构造了。

既然它什么都不做,C++给它打了个补丁,允许它在类声明的变量进行赋值,这里的赋值是缺省值,是给初始化列表的。

(2)我们自己写的默认构造函数(全缺省构造函数)还有我们写的无参的构造函数

注意:默认构造函数只能存在一个,因为编译器会不知道调用哪个。

2.有参构造函数。

我们用类创造一个类对象时需要传参。

构造函数的作用例子,我们用栈实现队列

class Stack {//自定义类型

public:
	Stack(int b=3)
	{
		int* temp = (int*)malloc(sizeof(int) * b);
		if (temp == nullptr)
		{
			perror("malloc:");
			exit(-1);
		}
		a= temp;
		top= 0;
		capacity = b;

	}
	
	
private:
	int capacity;
	int* a;
	int top;
	
};

//对于内置类型调用默认构造函数,对于自定义类型调用它的构造函数

class	MyQueue {          

	Stack push;//自定义类型,调用类Stack的构造函数
	Stack top;
	int _size;//内置类型,调用MyQueue类的默认构造函数
	};

总结而言,就是一个类中内置类型调用它自己的构造函数,对于自定义类型调用那个类型自己的构造函数 ,当一个类中既有自定义类型又有内置类型时而且没有写构造函数时,有些编译器(VS)会优化也会对内置类型进行初始化,然而并不是所有编译器都会优化,我们当对这样的内置类型当成不处理。这里的_sizeC++语法上来编译器自己生成的默认构造函数是不会初始化它的。

二、析构函数

1.什么是析构函数

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

 2.特点

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。

注意:析构 函数不能重载

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

3.析构函数的调用过程(可通过调试)

如果有一个 MyStack类对象,main函数在结束的时候会调用MyQueue的析构函数,而myStack push和myStack top它的析构函数是通过mian调用MyQueue的析构函数间接调用的,我们可以通过调试可以查看调用过程。

4.总结

对于一个类的内置类型,调用默认的析构函数,自定义类型调用它的析构函数

其实对于内置类型,我们可以不写析构函数直接用默认生成的析构函数,它会处理回收内置类型变量。然而对于栈这样的类需要而外开辟空间的,我们必须写它的析构函数,不然会造成内存泄漏。

(如果我们不写stack的析构函数,那编译器调用它的时默认的析构函数,只是把int*a开变量空间给回收,但是另外malloc()开辟的空间还是存在的,这就会造成内存泄漏)。

三,   拷贝构造函数

1.拷贝构造函数用处

那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

Date d1;//对象d1

Date d2(d1);

Date d3=d1;

拷贝构造函数就是将一个对象变成两个,它们都是一样的,就像双胞胎兄弟。

2.特点

拷贝构造函数是构造函数的重载,它的函数形参是引用&。它是单个形参,另一个形参在用已存 在的类类型对象创建新对象时由编译器自动调用(隐藏的this指针)。如果我们写了复制构造函数,传的值不是引用就会发生无限递归问题,因为调用这个函数参数先要拷贝一份对象,要拷贝对象就要调用拷贝构造..........

3.对于默认生成拷贝构造函数完成的是值拷贝又叫浅拷贝,有点像C语言函数传参数传的是传结构体,形参是实参的拷贝按照字节序拷贝。

3.拷贝构造函数主要用途

拷贝构造函数主要处理的是一个类中有另外开辟一段连续空间,用默认生成的拷贝构造它是值拷贝,两个对象的*a都指向同一块空间,程序结束时会各自调用析构函数,连续释放了同一块空间这是错误的

 这里我们没有写拷贝构造,调用的是编译器自己生成的拷贝构造是值拷贝,2个对象调用了两次析构函数,释放了同一个空间两次。

我们可以用拷贝构造函数解决这个问题

四.赋值运算符重载

关键字:operator+重载的运算符符号

1.重载的规则

(1)必须有一个类的参数

(2)不是所有符号都可以重载 .* :: sizeof ?: . 注意以上5个运算符不能重载。

    注意:*是可以重载的,因为*既可以代表运算符也可以指针的解引用。

(3)不能改变操作符,比如operator@,编译器识别不了。

(4)用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

在c语言中,我们学到的内置类型它们用到的操作符+ - *等,如果我们要操作自定义类型,就需要对运算符重载。

2.重载<<和>>

class Date {
public:
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	
	void print()const//权限平移const Date* const this
	{

		cout << _year << "/" << _month << "/" << _day;
	}

/*ostream& operator<<(ostream& out)//不要把<<的重载变成成员函数
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
	return out;
}*/

	friend ostream& operator<<(ostream& out, const Date& d);//声明为有友元,可访问私有
	friend istream& operator>>(istream& in, Date& d);

private:
	int _year;
	int _month;
	int _day;

};
 ostream& operator<<(ostream& out,const Date& d)
{
	out <<d. _year << "年" <<d. _month << "月" <<d. _day << "日" << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

ostream是输出流对象,istream是输入流对象。operator<<和opeartor>>我们把这两个定义为全局函数,并且在类中把它们声明为这个类的友元,就可以访问私有成员,如果把它定义在类里面,它就变成成员函数了,成员函数虽然通过隐藏的*this访问私有,如果连续输出还要加括号保证优先级问题和返回值

五. const成员函数

用const修饰类中的成员函数,实际是修饰那个隐藏的this指针。如果不用const修饰,const修饰的对象调用该成员函数就会报错,这里涉及到const权限扩大的问题。

const  Date d1(2020,2,2)------->指针类型为const Date*

Date* this--------->类型为Date* 

const Date*-------->Date*权限放大,这是错误的,权限可以平移和缩小,不可以放大。

1.成员函数的定义原则

const成员函数,可以保护对象不被修改,增加了安全性。

1.能被定义成const成员函数的函数最好加上const,这样const对象和非const对象都可以调用const成员函数

2.要修改成员变量的函数不加const

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。它们构成重载。

class Date
{
public :
 Date* operator&()
 {
 return this ;

 }
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};

注意:如果写了其中一个重载,编译器就不会生成默认成员函数,非const对象和const对象取地址都会调用写的那个函数,如果没写或者写了两个就会各自匹配各自类型的那个函数(const和非const).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值