咱们一起学C++ 第二百八十三篇之C++容器所有权控制与值存储方式详解
大家好!C++学习的过程中,容器的使用和管理是非常重要的部分,其中所有权控制以及对象在容器中的存储方式是两个关键知识点。今天咱们就一起来深入探讨这两个方面,希望能和大家共同进步,让我们对C++的理解更上一层楼!
一、容器所有权的灵活控制
在C++编程中,容器与所存储对象之间的所有权关系至关重要。OwnerStack
类为我们展示了一种灵活控制所有权的方式。
(一)所有权的默认与修改机制
OwnerStack
类通过构造函数的参数和owns()
函数来控制容器对所存储对象的所有权。默认情况下,容器拥有它所存储的对象,就像一个“主人”默认拥有自己的物品一样。在Stack(bool own = true): head(0), own(own)
构造函数中,own
参数的默认值为true
,这意味着创建Stack
对象时,它默认会销毁自己存储的对象。
但如果我们有特殊需求,也可以修改这个默认行为。比如,当我们创建Stack<AutoCounter> ac2(false);
时,通过将构造函数参数设为false
,表明ac2
这个容器不会销毁它里面存储的对象。这样,ac2
就像是一个“临时保管者”,它只负责存放对象,而不负责销毁对象。
(二)所有权对对象销毁的影响
在Stack
类的析构函数~Stack()
中,所有权的状态决定了对象的销毁方式。如果own
为true
,容器在销毁时会逐个删除它所存储的对象;如果own
为false
,容器在销毁时不会删除对象,就像“临时保管者”离开时,不会随意处理保管的物品一样。
在pop()
函数中也是如此,当从容器中弹出对象时,如果容器拥有所有权,弹出操作可能会涉及到对象的销毁;如果不拥有所有权,弹出操作仅仅是将对象从容器中移除,而不会销毁对象。
(三)测试所有权控制
通过OwnerStackTest.cpp
中的测试代码,我们可以更直观地理解所有权控制的效果。在这个测试中,创建了两个Stack<AutoCounter>
容器,ac
默认拥有对象的所有权,而ac2
通过构造函数参数设置为不拥有所有权。在添加和弹出对象的过程中,AutoCounter
类的对象创建和销毁机制会被触发,从而可以清晰地看到所有权控制对对象生命周期的影响。例如,当从ac2
中弹出对象时,由于ac2
不拥有对象的所有权,所以对象不会被销毁,而ac
容器在销毁时会自动销毁它所拥有的对象。
二、以值存放对象的容器实现
在C++中,除了存储对象指针,以值的方式将对象存放在容器中也是一种常见的需求。ValueStack
类为我们展示了这种存储方式的实现。
(一)对象存储与初始化
ValueStack
类使用数组T stack[ssize];
来存储对象,其中T
是模板参数,表示存储对象的类型,ssize
是数组的大小,默认值为100。在构造函数Stack(): top(0){}
中,top
被初始化为0,表示栈为空。
在push(const T& x)
函数中,当向栈中添加对象时,会调用对象的赋值运算符T::operator=
将传入的对象x
存储到数组中。这就好比把一个物品原封不动地放进一个盒子里。同时,会检查栈是否已满,如果已满则会根据require
函数的设置进行相应处理。
(二)对象的访问与弹出
peek()
函数用于查看栈顶对象,但不弹出它,pop()
函数则用于弹出栈顶对象。在pop()
函数中,先检查栈是否为空,若不为空则返回栈顶对象,并将栈顶指针top
减1。需要注意的是,与存储指针的容器不同,以值存放对象时,弹出对象后,对象本身仍然存在,只是不再在栈中。这就像从盒子里拿出一个物品,物品本身还是存在的。
(三)对象拷贝构造的作用
在ValueStack
类的实现中,用于被包含对象的拷贝构造函数起着重要作用。当使用push()
函数添加对象时,对象在栈数组上的存储是通过对象的赋值操作完成的,这依赖于对象的拷贝构造函数。为了更好地理解这个过程,可以创建一个自定义类来测试,例如:
class MyClass {
public:
MyClass() {
std::cout << "MyClass constructor" << std::endl;
}
MyClass(const MyClass& other) {
std::cout << "MyClass copy constructor" << std::endl;
}
MyClass& operator=(const MyClass& other) {
std::cout << "MyClass assignment operator" << std::endl;
return *this;
}
~MyClass() {
std::cout << "MyClass destructor" << std::endl;
}
};
int main() {
ValueStack<MyClass> stack;
MyClass obj;
stack.push(obj);
return 0;
}
在这个示例中,当向ValueStack
中添加MyClass
对象时,会调用MyClass
的拷贝构造函数和赋值运算符,通过观察输出信息可以清晰地看到这个过程。
三、总结
今天我们学习了C++容器的所有权控制和以值存放对象的相关知识。所有权控制让我们可以灵活管理容器与对象之间的关系,避免因对象销毁不当而导致的问题;以值存放对象则为我们提供了一种简单直接的对象存储方式。这些知识对于我们编写高效、可靠的C++代码非常重要。
写作不易,如果这篇文章对你学习C++有所帮助,希望你能点赞支持,也欢迎在评论区分享你的学习心得和疑问。记得关注我的博客,后续我会分享更多C++相关的知识,咱们一起在编程的道路上不断探索、共同进步!