C++构造函数

前言

class Stack
{
public:
	void Init(int defaultcapacity=4)
	{
		_a = (int*)malloc(sizeof(int) * defaultcapacity);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = defaultcapacity;
	}

	void push(int x)
	{
		_a[_top] = x;
		_top++;
	}

	void pop()
	{
		_top--;
	}

	void print()
	{
		cout << _a[_top-1] << endl;
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1;
	st1.Init();
	st1.push(1);
	st1.push(2);
	st1.push(3);


	st1.print();
	st1.pop();
	st1.print();

	st1.Destroy();
	return 0;
}

上述是我们用c++实现的一个栈,我们能看到,在上述代码中,需要先调用函数Init,进行初始化,使用结束之后又要调用函数Destroy进行清理。如果有时候忘记初始化就会报错,而忘记Destroy则会出现内存泄漏,是非常麻烦的事情。所以,基于此,C++内置了构造函数和析构函数,我们来看看是怎么使用的,以及注意事项。

构造函数的特性

构造函数有如下特性:

1、函数名和类名相同

2、没有返回值,也不需要写void

3、对象实例化的时候编译器会自动调用构造函数

4、构造函数可以重载

5、如果类中没有显式定义构造函数,则c++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

根据上面的情况,我们来写一个构造函数。

	Stack()
	{
		_a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = 4;
	}

我们来看看,函数名就是类名,所以是Stack,没有返回值,不需要写void。前面两点已经满足,我们观察这个构造函数,发现它其实和我们上面写的Init好像是一摸一样的,没有任何区别。就是自己实现的初始化。我们再来看看,说对象实例化的时候编译器会自动调用构造函数,那么怎么证明呢?我们来看运行结果。

class Stack
{
public:
	Stack()
	{
		cout << "Stack()" << endl;
		_a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = 4;
	}

	void push(int x)
	{
		_a[_top] = x;
		_top++;
	}

	void pop()
	{
		_top--;
	}

	void print()
	{
		cout << _a[_top-1] << endl;
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1;
	st1.push(1);
	st1.push(2);
	st1.push(3);


	st1.print();
	st1.pop();
	st1.print();

	st1.Destroy();
	return 0;
}

我们明明没有调用构造函数,最后通过运行的结果来看,确实是编译器自动调用了。 

最后说构造函数可以重载,我们来探索一下,观察下面的代码。

class Stack
{
public:
	Stack()
	{
		cout << "Stack()" << endl;
		_a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = 4;
	}

	Stack(int n)
	{
		cout << "Stack(int n)" << endl;
		_a = (int*)malloc(sizeof(int) * n);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = n;
	}

	void push(int x)
	{
		_a[_top] = x;
		_top++;
	}

	void pop()
	{
		_top--;
	}

	void print()
	{
		cout << _a[_top-1] << endl;
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
//private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1;
	Stack st2(10);
	return 0;
}

观察运行结果,表示确实可以函数重载。

注意事项:构造函数的调用不是st1.Stcak(),而是直接Stcak st1,或者Stack St1(10);如果是无参调用的时候就是Stcak st1,可能有小伙伴会问,为什么不能是st1.Stack()。如果是这样的话和st1.Init()有什么区别呢,那为什么还要设置一个构造函数出来?当然,更不能是Stack st1(),这样的话,会和函数声明很像,会分不清。所以记住,对象后面不用加()!!!

对于第五点,我们可以先来看一个经典的日期类。

class Data
{
public:
	Data()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

在上面的代码中,我们可以看到我们自己写了一个无参的构造函数并且都初始化为1了,那我们来看看运行的结果是什么。

 不出所料,打印的结果就是我们所写构造函数初始化的值。如果我们不写构造函数,调用编译器自动生成的会怎么样呢?

给出以下代码,观察运行结果。

class Data
{
public:
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

 根据运行结果,能看到编译器自动给的构造函数会初始化成随机值。可能有小伙伴就会说了,这不扯吗?初始化成随机值了可还行,确实这里给的不太好,所以在c++11中打了一个补丁,内置类型成员变量可以给缺省值,这样既可以初始化成随机值,又可以初始化成缺省值。具体可查看以下代码。

class Data
{
public:
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year=1;
	int _month=1;
	int _day=1;
};

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

 

如果是非内置类型呢?非内置类型就可以不用写构造函数了,例如下面这个代码。

class Stack
{
public:
	Stack()
	{
		cout << "Stack()" << endl;
		_a = (int*)malloc(sizeof(int) * 4);
		if (_a == nullptr)
			perror("malloc fail");
		_top = 0;
		_capacity = 4;
	}

	void push(int x)
	{
		_a[_top] = x;
		_top++;
	}

	void pop()
	{
		_top--;
	}

	void print()
	{
		cout << _a[_top - 1] << endl;
	}
	void Destroy()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}	
private:
	int* _a;
	int _top;
	int _capacity;
};


class Myqueue
{
public:
	Stack stackpush;
	Stack stackpop;
};

int main()
{
	Myqueue q1;

	return 0;
}

观察调试。

 最终成为了这个样子,这不就是我们之前Stack的构造函数吗,所以虽然非内置类型不用写,但是最终还是会回归到内置类型。

 上面提到一个概念,叫做内置类型。我们可以解释以下,什么是内置类型:int,char,double,指针都属于内置类型。非内置类型就是自定义类型,用struct、class定义的类型。

默认构造函数

默认构造函数只能有一个。那么什么是默认构造函数呢?默认构造函数:无参构造函数、全缺省构造函数、编译器自动生成的默认构造函数。

根据前面的分析,大家知道为什么吗?

因为:如果我们自己写了构造函数,那么编译器就不会自动生成默认构造函数,而无参构造函数和全缺省构造函数存在调用歧义,所以他们两个也只能存在一个。一山不容二虎。

class Data
{
public:
	Data()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	Data(int year = 2, int month = 2, int day = 2)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year=1;
	int _month=1;
	int _day=1;

};

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

如果我们这样写,但是在生成对象的时候,全缺省,就会出现错误。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值