类与对象(2)

1.构造函数:

构造函数 是一个 特殊的成员函数,名字与类名相同 , 创建类类型对象时由编译器自动调用 ,以保证
每个数据成员都有 一个合适的初始值,并且 在对象整个生命周期内只调用一次
特点:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器 自动调用 对应的构造函数。
4. 构造函数可以重载。
class Date
{
public:
	Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
    Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}


      private:
	//会生成默认构造
	//对内置类型不做处理
	int _year;
	int _month;
	int _day;
}

构造函数其实就是相当于一个自动调用的初始化,如果我们开始使用Date,那么它将自己给其赋值

我们接下来要注意几个点

为什么调用无参构造函数我们不加括号,也就是的d1后面为什么不加(),因为这很难与自己的函数声明区分开,加了括号,此时该函数没有参数,并且按照声明来看我们就是返回了一个Date 类型,但是我们这时候就是调用一个构造函数;

写了两个构造函数,一个有参一个无参,这有没有问题,其实是有的;

int main()
{
  Data d1;
return 0;
}

这个时候我们该调用哪个呢,调用无参和有参都可以,所以在这种情况下,两者是不能同时存在的既然如此我们就写那个有参的,因为都是能使用,所以我们直接写一个全缺省。

5. 如果类中没有显式定义构造函数,则 C++ 编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
这个特性大家可以删掉自己写的构造函数,在c++官方规定中
<1>.内置类型的构造函数是不做处理的,所以一般是随机值。<2>还有我们自定义的类型中,也会调用默认构造函数(无参构造,自动生成的构造,全缺省的构造)。
 
虽然不做处理但是后来有了补丁方法
class Date
{
public:
	

      private:
	//会生成默认构造
	//对内置类型不做处理
	int _year=1;
	int _month=0;
	int _day=0;
}

2:

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

特点:
1. 析构函数名是在类名前加上字符 ~
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载
4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数。
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_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;
};

特性5:如果没有定义析构函数,那么编译器就会自己生成一个,那为什么我们还要自己写一个,那还有有不同的情况啊,比如链表,那不得一个一个释放啊。

总结:

<1>.有资源需要清理,就要写析构,比如:Stack,List

<2>.两种情况不用写:a.无资源清理的   b.内置类型成员没有资源需要清理的,剩下的都是自定义的成员,如MyQueue,就是用栈实现队列,栈里面自己写了,就可以自己调用.

3.拷贝函数

1. 拷贝构造函数 是构造函数的一个重载形式
2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错
因为会引发无穷递归调用。
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	Stack(const Stack& d)
	{
		_array = (DataType*)malloc(sizeof(DataType) * d._capacity);
		if (_array == NULL)
		{
			perror("fail");
		}
		_size = d._size;
		_capacity = d._capacity;
	}
	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;
};

其实拷贝函数是构造函数中的一种;

如果按照下面的写法为什么会引发递归呢:

int main()
{
  Stack s1;
  Stack s2=s1;
 return 0;
}

首先要知道,自定义类型拷贝时候会调用拷贝函数

我们给s2进行拷贝构造的时候,它是一个自定义类型 Stack ,因为函数栈帧上我们知道,传值调用的时候形参和实参并不是一块空间,把实参的内容拷贝到形参,所以给s2拷贝时候先得给d拷贝了。我们d也是自定义类型会引发一个拷贝,但是传的s1不是实参,为了和实参一样,这一次的d他又进行了拷贝构造,拷贝构造时候又出现了d,可惜还不是实参,这样一直进行递归。如果我们用的是引用,调用拷贝函数的时候,第一次就是s1的实参,直接就可以使用,把s1的实参直接拷贝给d,d再给s2使用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值