取消引用不适用于集合中的智能指针

Dereferencing not working for smart pointer in set

#include<iostream>
#include<set>
template <typename T>
/* Simple smart pointer class */
class SmartPtr
{
    T *ptr;
public:
    explicit SmartPtr(T *p = NULL) { ptr = p; }
    ~SmartPtr() { delete(ptr); }
    T & operator * () { return *ptr; }

    T * operator -> () { return ptr; }

};

class simple {

private:
    int x;
public:
    simple(int y = 0) :x(y) {}
    int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;



int main() {


    std::set<simplePtr> st;
    simplePtr p1 = simplePtr(new simple(5));
    simplePtr p2 = simplePtr(new simple(5));
    simplePtr p3 = simplePtr(new simple(5));
    simplePtr p4 = simplePtr(new simple(5));

    std::cout << p1->getX();  <-- working fine
    st.insert(p1);
    st.insert(p2);
    st.insert(p3);
    st.insert(p4);

    for (std::set<simplePtr>::iterator it = st.begin(); it != st.end(); ++it)
    {
        std::cout << it->getX();  // Not working??
    }
}

Visual Studio 2013 年编译失败并出现错误:

Error   C2039   getX: is not a member of SmartPtr<simple>

在 linux:

error: ‘const class SmartPtr<simple>’ has no member named ‘getX’

这是迭代器的问题吗??

您的迭代器需要取消引用,此时您将获得一个指针。然后需要取消引用。所以:

    std::cout << (*it)->getX();

您可以将 it->getX() 视为 (*it).getX() 的语法糖。 [原则上,class 可以不一致地重载 ->*(取消引用)运算符,但 std::set<T>::iterator 并不违反该约定,这并不奇怪。因此,在您的情况下,*it 被取消引用为类型 const SmartPtr<simple>& 的左值,并且应用于它的 .getX() 失败,因为 SmartPtr 没有 [=23] =] 方法。因为,相反,您的意思是访问获得的 SmartPtr 指向的对象,因此您必须再添加一个取消引用级别:

Correction 1

Replace it->getX() with (**it).getX() or (*it)->getX().

还有一个问题,但是 - *it 导致 const SmartPtr(是的,std::set' s 非常量迭代器不提供对容器元素的写访问,否则您可能会破坏容器中元素的正确排序)。但是 SmartPtr 中的 ->*(取消引用)运算符的定义方式是它们只能在非 const 对象上调用。要解决这个问题,您必须使这两个函数 const:

Correction 2 (in SmartPtr<T>)

//                 vvvvv
T & operator * ()  const { return *ptr; }
T * operator -> () const { return ptr; }
//                 ^^^^^

进行第二次更正后,您可以将旧式 for 循环替换为范围 for 循环:

for (const simplePtr& p : st)
{
    std::cout << p->getX();
}

仍然,您的程序不会编译 - SmartPtr<T> 对象不能放在 std::set 中,因为它们 不可比较 。通过定义 operator<():

来解决这个问题

Correction 3

Add to SmartPtr<T>:

bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }

此时您的代码可以编译,但很可能无法正常工作。原因是 SmartPtr<T> 的复制语义由编译器自行决定,这不符合您的意图。这很容易通过发现 Rule of Three, Four and Five 的违规行为来猜测 - 您的 class 定义了析构函数但未能定义复制 and/or 移动构造函数和赋值运算符。因此,您的代码执行双重删除,因此不能保证任何明确定义的行为。

Correction 4

Fix the copy semantics of SmartPtr<T>.

我 "fixed" 您的代码通过将移动语义分配给 SmartPtr(这需要在 insert()-ing 到 std::set 时添加 std::move()):

#include<iostream>
#include<set>

template <typename T>
class SmartPtr
{
    T *ptr;
public:
    explicit SmartPtr(T *p = NULL) { ptr = p; }
    ~SmartPtr() { delete(ptr); }
    SmartPtr(const SmartPtr& other) = delete;
    SmartPtr(SmartPtr&& other) : ptr(other.ptr) { other.ptr = NULL; }
    SmartPtr& operator=(SmartPtr other)
    {
        std::swap(ptr, other.ptr);
        return *this;
    }

    T & operator * () const { return *ptr; }
    T * operator -> () const { return ptr; }

    bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
};

class simple {
    int x;
public:
    simple(int y = 0) : x(y) {}
    int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;

int main() {
    std::set<simplePtr> st;
    simplePtr p1 = simplePtr(new simple(5));
    simplePtr p2 = simplePtr(new simple(5));

    st.insert(std::move(p1));
    st.insert(std::move(p2));

    for (const simplePtr& p : st)
    {
        std::cout << p->getX();
    }
    return 0;
}

对于初学者,在集合中使用它之前,您必须为 class SmartPtr 定义 operator <

其次你必须声明成员函数getX like

int getX() const { return x; }

而且你至少要写得像

std::cout << ( *it )->getX();