C++堆栈实现及拷贝构造函数的使用注意事项

本文对比了C++与C语言实现堆栈的不同之处,重点介绍了C++堆栈实现的封装性和安全性优势,包括自动调用构造函数和析构函数、简化类方法调用以及正确的拷贝构造函数实现。

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

此例C++的堆栈实现与之前C的堆栈实现相比的优点及自身特点:
        1、良好的封装性和安全性,这也是C++语言本身具有的特点,在C实现的栈的方法中,需要判断入参栈指针合法性,而C++中无需,因为栈指针是成员变量,不是显式入参,不会勿传其他指针。比如销毁堆栈时,C++可以利用析构函数,无需判断堆栈指针S是否为NULL,而若C代码实现销毁,则需要一段判断堆栈指针S是否为NULL的代码,否则后续代码执行会发生异常;又如清空堆栈函数,在C++中无需判断S指针是否存在,因为要调用这个清空函数,堆栈类必须先已经实例化了,这就保证了调用清空函数前S已经存在并有所指向,而C中则S指针是否存在得在清空函数中进行判断,排除S指针不存在的情况及S指针为野指针的情况才能进行后续操作。所以说C++的封装性具有良好的安全性。
        2、实例初始化自动调用构造函数,销毁自动调用析构函数,而不用像C语言实现里需要显式调用,增加代码量,易忘销毁内存而造成内存泄露。
        3、类的方法调用本类的方法可以不像其他域中用"实例.方法"来调用来的复杂,因为隐含this指针,比如本例中m_Stack.Run()里调用Push(),若在main函数里调用则是Stack m_Stack; m_Stack.Push(X)。所以建议不要再main里写“实例.方法”等细的模块功能操作。面向对象的编程应该是立即把main函数转入某一个类实例化的方法中,然后由这个实例的方法进行环境初始化,实例化其他类,进行各种操作。main相当于一个入口地址,立即跳转,然后不再使用。
        4、另外特别要注意堆栈实例进行拷贝时,默认拷贝构造函数(赋值构造函数)具有局限性,必要得自己重写。

以下是使用默认赋值构造函数的运行结果:
可以看出m_Stack的指针指向了s_Stack指针指向的内存区,以致后来s_Stack的压栈影响了m_Stack的堆栈情况,另外m_Stack的指针原先指向的内存并没有释放,所以既不符合实际想要结果,又非法。


重写赋值构造函数后运行结果为:





这时就实现了我们本来想要的结果,即堆栈的内容进行拷贝。

完整代码如下:
/*
============================================================================
Name        : 堆栈(堆栈,堆栈,堆中开的栈空间,所以叫堆栈)
Author      : Swair Fang
Version     : 1.1
Copyright   : by Swair Fang
Description : 堆栈实现, Ansi-style
功能:
	1.压栈
	2.出栈
	3.查看栈顶
	4.打印栈
============================================================================
 */
#include 
#include 
#include 
using namespace std;


#ifndef _Stack_H
#define _Stack_H
typedef int ElementType;

//构建节点
typedef struct Node
{
	ElementType Element;
	struct Node *Next;
};
//构建堆栈
class Stack
{
private:
	struct Node *S;
	struct Node *P;
public:
	Stack();
	~Stack();
	void operator=(Stack &right);
	void Push(ElementType X);
	void Pop(ElementType *pX);
	void Top(ElementType *pX);
	void MakeEmpty();
	void Print();
	void Run();
	void Empty();
};
Stack::Stack()
{
	printf("Create Stack\n");
	S=new Node;
	if(S==NULL)
		printf("Out of space!!!");
	S->Next=NULL;
}
Stack::~Stack()
{
	printf("Destroy Stack\n");
	if(S==NULL)
		printf("Must use CreateStack first");
	else
	{
		Node *Tmp;
		P=S->Next;
		S->Next=NULL;
		while(P!=NULL)
		{
			Tmp=P->Next;
			delete P;
			P=Tmp;
		}
		delete S;
	}
}
void Stack::Empty()
{
	printf("Empty Stack\n");
	if(S==NULL)
		printf("Must use CreateStack first");
	else
	{
		Node *Tmp;
		P=S->Next;
		S->Next=NULL;
		while(P!=NULL)
		{
			Tmp=P->Next;
			delete P;
			P=Tmp;
		}
	}
}
//由于默认的赋值构造函数是完全赋值,即本实例的变量数值会被另一个实例的变量数值完全取代,
//特别是指针,也就是说本实例的指针值等于另一实例的指针值,即两指针指向了同一块内存,
//而被赋值的实例的指针指向的内存并没有释放,这就会引起问题,所以得自己写赋值构造函数
//使得实例之间的赋值为实际意义的变量内容的赋值,而非完全赋值。
void Stack::operator=(Stack &right)
{
	printf("call assignment\n");
	Empty();
	P=S;
	right.P=right.S->Next;
	while(right.P!=NULL)
	{
		struct Node *Tmp=new Node;
		Tmp->Element=right.P->Element;
		Tmp->Next=NULL;
		P->Next=Tmp;
		P=P->Next;
		right.P=right.P->Next;
	}
}
//1.压栈
void Stack::Push(ElementType X)
{
	P=new Node;
	if(P==NULL)
	{
		printf("Out of space!!!");
		exit(1);
	}
	P->Element=X;
	P->Next=S->Next;
	S->Next=P;
}
//2.出栈
void Stack::Pop(ElementType *pX)
{
	if(S==NULL||S->Next==NULL)
		printf("Empty stack");
	else
	{
		P=S->Next;
		S->Next=S->Next->Next;
		ElementType X=P->Element;
		free(P);
		*pX=X;
	}
}
//3.查看栈顶
void Stack::Top(ElementType *pX)
{
	if(S==NULL||S->Next==NULL)
		printf("Empty stack");
	else
	{
		*pX=S->Next->Element;
	}
}
//4.打印栈
void Stack::Print()
{
	if(S->Next==NULL)
		printf("链表为空\n");
	else
	{
		P=S->Next;
		while(P!=NULL)
		{
			char *cp=(char*)P;
			printf("0x%x: %d\n  ",cp,P->Element);
			P=P->Next;
		}
		printf("\n");
	}
}
void Stack::Run()
{
	Stack m_Stack;
	Push(3);
	Push(23);
	Push(33);
	m_Stack.Push(63);
	m_Stack.Push(61);

	printf("\ns_Stack情况:\n");
	Print();
	printf("m_Stack情况:\n");
	m_Stack.Print();

	printf("\nm_Stack拷贝给s_Stack:\n");
	*this=m_Stack;

	printf("\ns_Stack情况:\n");
	Print();
	printf("m_Stack情况:\n");
	m_Stack.Print();

	printf("m_Stack压栈\n");
	m_Stack.Push(333);
	m_Stack.Push(424);
	m_Stack.Push(32);
	m_Stack.Push(12);
	m_Stack.Push(53);
	m_Stack.Push(123);

	printf("\ns_Stack情况:\n");
	Print();
	printf("m_Stack情况:\n");
	m_Stack.Print();
}
#endif/*_Stack_H*/
class MAINServer
{
public:
	void Start();
};
void MAINServer::Start()
{
	Stack m_Stack;
	Stack s_Stack;
	m_Stack.Push(3);
	m_Stack.Push(23);
	m_Stack.Push(33);
	s_Stack.Push(63);
	s_Stack.Push(61);

	printf("\nm_Stack情况:\n");
	m_Stack.Print();
	printf("s_Stack情况:\n");
	s_Stack.Print();

	printf("\ns_Stack拷贝给m_Stack:\n");
	m_Stack=s_Stack;

	printf("\nm_Stack情况:\n");
	m_Stack.Print();
	printf("s_Stack情况:\n");
	s_Stack.Print();

	printf("s_Stack压栈\n");
	s_Stack.Push(333);

	printf("\nm_Stack情况:\n");
	m_Stack.Print();
	printf("s_Stack情况:\n");
	s_Stack.Print();

	printf("--------------\n");

	s_Stack.Run();
}

int main()
{
	MAINServer MS;
	MS.Start();
	cout << "Hello World!!!" << endl; // prints Hello World!!!
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值