使用动态内存的一个常见原因是允许多个对象共享相同的状态。
例如,假定我们希望定义一个名为
Blob
的类,保存一组元素。与容器不同,我们希望Blob
对象的不同拷贝之间共享相同的元素。即,当我们拷贝一个Blob
时,原Blob
对象及其拷贝应该引用相同的底层元素。现在我们先定义一个管理string
的类,此版本命名为StrBlob
。
定义 StrBlob类
实现一个新的集合类型的最简单方法是使用某个标准库容器来管理元素。采用这种方法,我们可以借助标准库类型来管理元素所使用的内存空间。在本例中,我们将使用vector
来保存元素。
但是,我们不能在一个Blob
对象内直接保存vector
,因为一个对象的成员在对象销毁时也会被销毁。例如,
- 假定
b1
和b2
是两个Blob
对象,共享相同的vector
。 - 如果此
vector
保存在其中一个Blob
中——例如b2
中,那么当b2
离开作用域时,此vector
也将被销毁,也就是说其中的元素都将不复存在。为了保证vector
中的元素继续存在,我们将vector
保存在动态内存中。
为了实现我们所希望的数据共享,我们为每个StrBlob
设置一个shared_ptr
来管理动态分配的 vector
。此 shared_ptr
的成员将记录有多少个StrBlob
共享相同的vector
,并在vector
的最后一个使用者被销毁时释放vector
。
我们还需要确定这个类应该提供什么操作。当前,我们将实现一个vector
操作的小的子集。我们会修改访问元素的操作(如 front
和 back
): 在我们的类中,如果用户试图访问不存在的元素,这些操作会抛出一个异常。
我们的类有一个默认构造函数和一个构造函数,接受单一的 initializer_list<string>
类型参数。此构造函数可以接受一个初始化器的花括号列表。
initializer_list
对象中的元素永远是常量值,我们无法改变initializer_list
对象中元素的值。在进行函数调用的时候需要使用花括号将所有的参数括起来。
class StrBlob{
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob();
StrBlob(std::initializer_list<std::string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const std::string &t){ data->push_back(t); }
void pop_back();
//元素访问
std::string& front();
std::string& back();
private:
std::shared_ptr<std::vector<std::string>> data;
//如果data[i]不合法,抛出一个异常
void check(size_type i, const std::string &msg) const;
};
在此类中,我们实现了size
、empty
和 push_back
成员。这些成员通过指向底层vector
的 data
成员来完成它们的工作。例如,对一个 StrBlob
对象调用size()
会调用data->size()
,依此类推。
StrBlob构造函数
两个构造函数都使用初始化列表来初始化其data
成员,令它指向一个动态分配的vector
。默认构造函数分配一个空vector
:
StrBlob::StrBlob() : data(make_shared<vector<string>>()){ }
StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) { }
接受一个initializer_list
的构造函数将其参数传递给对应的vector
构造函数。此构造函数通过拷贝列表中的值来初始化vector
的元素。
元素访问成员函数
pop_back
、front
和 back
操作访问vector
中的元素。这些操作在试图访问元素之前必须检查元素是否存在。由于这些成员函数需要做相同的检查操作,我们为StrBlob
定义了一个名为check
的private
工具函数,它检查一个给定索引是否在合法范围内。除了索引,check
还接受一个string
参数,它会将此参数传递给异常处理程序,这个string
描述了错误内容:
void StrBlob::check(size_type i, const string &msg) const
{
if(i >= data->size())
throw out_of_range(msg);
}
pop_back
和元素访问成员函数
首先调用check
。如果check
成功,这些成员函数继续利用底层vector
的操作来完成自己的工作:
string& StrBlob::front()
{
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back()
{
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
StrBlob 的拷贝、赋值和销毁
StrBlob
使用默认版本的拷贝、赋值和销毁成员函数来对此类型的对象进行这些操作。默认情况下,这些操作拷贝、赋值和销毁类的数据成员。
我们的StrBlob
类只有一个数据成员,它是shared_ptr
类型。因此,当我们拷贝、赋值或销毁一个 StrBlob
对象时,它的shared_ptr
成员会被拷贝、赋值或销毁。文章来源:https://www.toymoban.com/news/detail-427999.html
- 拷贝一个
shared_ptr
会递增其引用计数; - 将一个
shared_ptr
赋予另一个shared_ptr
会递增赋值号右侧shared_ptr
的引用计数,而递减左侧shared_ptr
的引用计数。 - 如果一个
shared_ptr
的引用计数变为0
,它所指向的对象会被自动销毁。因此,对于由StrBlob
构造函数分配的vector
,当最后一个指向它的StrBlob
对象被销毁时,它会随之被自动销毁。
注:仅供学习参考,如有不足,欢迎指正!文章来源地址https://www.toymoban.com/news/detail-427999.html
到了这里,关于C++ 使用动态内存创建一个类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!