取消引用不适用于集合中的智能指针
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();
#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();