65.9K
CodeProject 正在变化。 阅读更多。
Home

克隆智能指针(clone_ptr)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (10投票s)

2005年8月23日

CPOL

4分钟阅读

viewsIcon

81229

downloadIcon

786

一个行为类似于引用变量的智能指针。

引言

clone_ptr 类是一个智能指针,设计用于与 STL 容器一起使用,以便对对象本身进行比较和复制操作,而不是对象的地址。 它通过在执行这些操作时充当引用变量来实现这一点。 赋值操作会创建对象的唯一副本,因为 clone_ptr 表现为引用变量,并且它是一个非共享智能指针。

它能够将自身复制到正确的派生类型,而无需基类具有克隆方法。 此外,克隆指针具有用于赋值、等于和大于的运算符方法,这些方法调用对象的赋值运算符。 这意味着当对克隆指针的容器执行排序时,将对指向的对象进行排序,而不是指针地址。

与共享指针不同,clone_ptr 类不共享其指针,并且它基于严格的指针所有权逻辑。 这意味着一个克隆指针容器的内容可以复制到另一个容器,而无需两个容器指向相同的数据。

背景

我决定创建一个新的智能指针,因为常见的可用智能指针不太适合 STL 容器。 按照 C++ 标准,std::auto_ptr 根本不能与 STL 容器一起使用。 如果您需要将对象从一个容器复制到另一个容器,boost 的 shared_ptr 类并不是一个好的选择。

clone_ptr 类与其他智能指针(如 boost::shared_ptrauto_ptr)之间的一个主要逻辑区别是,clone_ptr 试图通过指针接口模拟引用变量(具体类型),而 boost::shared_ptrstd::auto_ptr 试图模拟指针。 模拟指针的缺点是它引入了通常没有益处,并且通常不利于所需效果的逻辑。

以下是容器中不需要的指针行为的示例。

    vector<std::shared_ptr<std::string> > vStrings;
    vStrings.push_back(std::make_shared<string>(std::string("Jill")));
    vStrings.push_back(std::make_shared<string>(std::string("Tom")));
    vStrings.push_back(std::make_shared<string>(std::string("David")));
    vStrings.emplace_back(new std::string("Zulma"));
    vStrings.emplace_back(new std::string("Yari"));
    vStrings.emplace_back(new std::string("Bill"));

    sort(vStrings.begin(), vStrings.end()); //Will NOT sort objects
    //Displays unsorted data
    for (size_t i = 0; i < vStrings.size(); ++i)
        cout << vStrings[i]->c_str() << "\n";

通常,在上面的代码中,期望的逻辑是对容器中的对象进行排序,但对于大多数智能指针,将对对象地址进行排序。 结果是输出的结果没有按名称排序。

使用 clone_ptr,比较是在指向的对象上进行的,而不是在指针的地址上进行的。 以下代码将输出按排序的名称。

    vector<clone_ptr<string> > vStrings;
    vStrings.push_back(new string("Jill"));
    vStrings.push_back(new string("Tom"));
    vStrings.push_back(new string("David"));
    vStrings.emplace_back(new string("Zulma"));
    vStrings.emplace_back(new string("Yari"));
    vStrings.emplace_back(new string("Bill"));

    sort(vStrings.begin(), vStrings.end());  //Sorts the object
    //Display sorted data based on the object
    for (size_t i = 0; i < vStrings.size(); ++i)
        cout << vStrings[i]->c_str() << "\n";

在智能指针的容器中,很少需要或期望对指针地址进行比较,而是更有益和实用的是让运算符将逻辑应用于解引用指针,而不是指针本身。 这就是 clone_ptr 类不尝试 100% 指针模拟,而是更像是混合体的原因,在有益时模拟指针,但在应用比较运算符时模拟引用变量类型。

尽管 clone_ptr 类是专门为与 STL 容器一起使用而设计的,但它仍然有用,并且在 STL 容器之外具有实际应用。

Using the Code

使用 clone_ptr 比其他智能指针有很多好处,但以下内容突出显示了一些主要好处。

clone_ptr 允许复制对象,而不是复制指针地址。

void CopyCorrectDerivedTypeDemo() {
std::vector < clone_ptr < BaseClass> >  vBaseClass; 
    //Setup data using base and derived type classes
    vBaseClass.push_back(new BaseClass( "1" )); 
    vBaseClass.push_back(new Derived_B( "2" )); 
    vBaseClass.push_back(new BaseClass( "3" )); 
    vBaseClass.push_back(new Derived_A( "4" )); 
    vBaseClass.push_back(new BaseClass( "5" )); 
    
    //Copy contents from one container to another
    std::vector < clone_ptr < BaseClass > >  
      vBaseClass_Copy(vBaseClass.begin(), vBaseClass.end()); 
    
    //Display results
    for (std::vector < clone_ptr < BaseClass > >::size_type i 
                   = 0;i < vBaseClass_Copy.size();++i)
    {
        vBaseClass_Copy[i]->WhoAmI();
    }

clone_ptr 的容器进行排序时,将对对象类型进行排序,而不是对指针地址进行排序。

void VectorSortDemo()
{
    std::vector < clone_ptr < BaseClass> >  vBaseClass; 
    //Setup data using base and derived type classes
    vBaseClass.push_back(new BaseClass( "3" )); 
    vBaseClass.push_back(new Derived_B( "2" )); 
    vBaseClass.push_back(new BaseClass( "1" )); 
    vBaseClass.push_back(new Derived_A( "5" )); 
    vBaseClass.push_back(new BaseClass( "4" )); 

    //Sort Data
    sort(vBaseClass.begin(), vBaseClass.end()); 

    //Display sorted data
    for (std::vector < clone_ptr < BaseClass > >::size_type 
                            i = 0;i < vBaseClass.size();++i)
    {
        vBaseClass[i]->WhoAmI();
    }

使用 clone_ptr 时,std::setstd::map 容器也会被正确排序。

void SetDemo()
{
    std::set < clone_ptr < BaseClass > >  sBaseClass; 
    //Setup data using base and derived type classes
    sBaseClass.insert(new BaseClass( "3" )); 
    sBaseClass.insert(new Derived_B( "2" )); 
    sBaseClass.insert(new BaseClass( "1" )); 
    sBaseClass.insert(new Derived_A( "5" )); 
    sBaseClass.insert(new BaseClass( "4" )); 

    //Display sorted data
    for(std::set < clone_ptr < BaseClass > >::iterator i = 
                sBaseClass.begin();i!=sBaseClass.end();++i)
    {
        (*i)->WhoAmI();
    }

相等运算符有助于确定一个容器是否等于另一个容器。

void ContainerEqualityDemo()
{
    list < clone_ptr < BaseClass > >  PtrsX;
    PtrsX.push_back(new BaseClass("1"));
    PtrsX.push_back(new BaseClass("2"));
    PtrsX.push_back(new BaseClass("3"));
    
    list < clone_ptr < BaseClass > >  PtrsY;
    PtrsY.push_back(new Derived_B("4"));
    PtrsY.push_back(new Derived_B("5"));
    PtrsY.push_back(new Derived_B("6"));
    
    list < clone_ptr < BaseClass > > PtrsZ;
    PtrsZ.push_back(new Derived_A("1"));
    PtrsZ.push_back(new Derived_A("2"));
    PtrsZ.push_back(new Derived_A("3"));
    
    bool IsEqual1 = (PtrsX == PtrsY); //Should be false
    bool IsEqual2 = (PtrsX == PtrsZ); //Should be true
    
    cout << "Should be false (0) IsEqual1 = " << IsEqual1 << endl;
    cout << "Should be true (1) IsEqual2 = " << IsEqual2 << endl;

相等运算符也有助于 STL 的替换和删除功能。

void RemoveFromListDemo()
{
    std::list < clone_ptr < BaseClass> >  lBaseClass; 
    lBaseClass.push_back(new BaseClass( "X" )); 
    lBaseClass.push_back(new Derived_B( "2" )); 
    lBaseClass.push_back(new BaseClass( "X" )); 
    lBaseClass.push_back(new Derived_A( "5" )); 
    lBaseClass.push_back(new BaseClass( "4" ));

    //Remove certain values
    lBaseClass.remove(clone_ptr < BaseClass > (new BaseClass("X"))); //Remove all X's
    
    //Display results
    for(std::list < clone_ptr < BaseClass > >::iterator 
        i = lBaseClass.begin();i!=lBaseClass.end();++i)
    {
        (*i)->WhoAmI();
    }

优点和缺点

缺点

复制容器时,shared_ptr 可能比 clone_ptr 使用更少的内存,因为它复制的是地址,而不是复制对象。

优点

在容器中复制对象时,会创建唯一的副本,可以在不影响原始对象的情况下对其进行操作。

产生预期的比较行为,因为比较运算符在对象上执行,而不是在指针上执行。

可以在 std::mapstd::set 中使用,以根据对象获得排序结果。

std::sort 一起使用并产生预期的行为。

结论

通过使用 clone_ptr,您可以获得指针和智能指针的优势,而没有排序问题和无法创建独立副本的缺点。

以上所有代码都在用于下载的演示项目中列出。

历史

  • 2021 年 7 月 21 日
    • 更新的使用示例
    • 添加了优缺点
    • 添加了澄清,主要问题是引用逻辑而不是指针逻辑
© . All rights reserved.