unique_ptr 如何同时支持 "dot" 和 "arrow" 调用以及未定义的方法?
How does unique_ptr support both "dot" and "arrow" invocation, and undefined methods?
据我所知,在 C++ 中,对任何对象的任何方法调用都必须在其 class 定义中预先定义。因此,当我查看 std::unique_ptr
.
时,它变得很有趣
似乎unique_ptr
同时支持"dot"(例如reset()
)和"arrow"操作。但是,"dot" 用于指针,而 "arrow" 用于 objects/references(我们可以做 ptr->MethodThatTheEncapsulatedClassSupports()
)。那么 unique_ptr
怎么可能既是指针又是对象呢?
第二个有趣的部分是,传递给 unique_ptr
的 class 可以是任意的。我可以在我的 class 中定义任何方法,而且我们似乎可以直接在 unique_ptr
实例上调用该方法。由于 C++ 没有像 Ruby 那样的动态方法调度机制(AFAIK C++ 方法调用是静态的并且必须定义,这是有道理的,因为 C++ 直接编译成机器代码),这怎么可能实现?
class 传递给 unique_ptr 的原因可以是任意的,因为 class 是一个模板参数。您应该阅读有关模板及其工作原理的信息,但本质上,在编译时自动为您使用的每个 class 提供一个 unique_ptr 版本。
-> 的工作方式是运算符重载。 unique_ptr 的实例不是指针,而是包含指针的对象,并且具有运算符重载,允许您使用 -> 访问该指针指向的对象中的数据。 .访问 unique_ptr 本身的方法。
这是因为 operator->
可以重载到 return 另一个对象或指针。然后递归地与 operator->
一起使用。
class Dest
{
public:
void test() {
std::cout << "Test Called\n";
}
};
class Forward
{
Dest d;
public:
Dest* operator->() {
return &d;
}
};
int main()
{
Forward f;
f->test(); // operator-> on the object `f` returns a pointer to
// an object of type `Dest*`. Now apply re-apply the
// operator-> to the result.
//
// Since it is already a pointer this means access the
// member (which in this case is a method call).
}
我能想到的最简单的智能指针实现是这样的:
#include <iostream>
struct A {
void moo(){std::cout << "MOO" << std::endl;}
};
template <typename T>
struct DumbPointer {
T* t;
DumbPointer(T* t) : t(t) {}
~DumbPointer(){delete t;}
T* operator->(){return t;}
void moo(){std::cout << "MOOMOO" << std::endl;}
};
int main() {
DumbPointer<A> f{new A()};
f.moo(); // prints MOOMOO
f->moo(); // prints MOO
return 0;
}
重载 ->
运算符,使其看起来像一个指针,而实际上 DumbPointer
本身是一个对象。有关重载运算符的更多详细信息,请参见例如here。相关部分是:
If a user-defined operator-> is provided, the operator-> is called again on the value that it returns, recursively, until an operator-> is reached that returns a plain pointer. After that, built-in semantics are applied to that pointer.
PS:上面的例子是有保留的。例如DumbPointer<A> g = f;
会导致不好的事情发生。
我发现 James O. Coplien 在他的书中的解释非常清楚易懂 Advanced C++
。这是解释;
重载 operator->
与其他重载 C++ 运算符的工作方式不同。对于所有其他运算符,定义运算符实现的主体对从运算中得到的值 return 具有最终控制权。对于 operator->
,return 值是一个中间结果,然后应用 ->
的基本语义,产生一个结果。所以,
class B {
...
};
class A {
public:
B *operator->();
};
int main() {
A a;
... a->b ...
}
表示如下:
- 在对象
a
上调用 A::operator->()
;
- 在
x
类型 B*
; 的临时 x
中捕获来自该调用的 return 值
- 计算
x->b
,得出结果。
这里,b
可以替换为 class B
的任何成员、数据或函数。如果 class B
重载 operator->
则再次应用上述三个步骤。
据我所知,在 C++ 中,对任何对象的任何方法调用都必须在其 class 定义中预先定义。因此,当我查看 std::unique_ptr
.
似乎unique_ptr
同时支持"dot"(例如reset()
)和"arrow"操作。但是,"dot" 用于指针,而 "arrow" 用于 objects/references(我们可以做 ptr->MethodThatTheEncapsulatedClassSupports()
)。那么 unique_ptr
怎么可能既是指针又是对象呢?
第二个有趣的部分是,传递给 unique_ptr
的 class 可以是任意的。我可以在我的 class 中定义任何方法,而且我们似乎可以直接在 unique_ptr
实例上调用该方法。由于 C++ 没有像 Ruby 那样的动态方法调度机制(AFAIK C++ 方法调用是静态的并且必须定义,这是有道理的,因为 C++ 直接编译成机器代码),这怎么可能实现?
class 传递给 unique_ptr 的原因可以是任意的,因为 class 是一个模板参数。您应该阅读有关模板及其工作原理的信息,但本质上,在编译时自动为您使用的每个 class 提供一个 unique_ptr 版本。
-> 的工作方式是运算符重载。 unique_ptr 的实例不是指针,而是包含指针的对象,并且具有运算符重载,允许您使用 -> 访问该指针指向的对象中的数据。 .访问 unique_ptr 本身的方法。
这是因为 operator->
可以重载到 return 另一个对象或指针。然后递归地与 operator->
一起使用。
class Dest
{
public:
void test() {
std::cout << "Test Called\n";
}
};
class Forward
{
Dest d;
public:
Dest* operator->() {
return &d;
}
};
int main()
{
Forward f;
f->test(); // operator-> on the object `f` returns a pointer to
// an object of type `Dest*`. Now apply re-apply the
// operator-> to the result.
//
// Since it is already a pointer this means access the
// member (which in this case is a method call).
}
我能想到的最简单的智能指针实现是这样的:
#include <iostream>
struct A {
void moo(){std::cout << "MOO" << std::endl;}
};
template <typename T>
struct DumbPointer {
T* t;
DumbPointer(T* t) : t(t) {}
~DumbPointer(){delete t;}
T* operator->(){return t;}
void moo(){std::cout << "MOOMOO" << std::endl;}
};
int main() {
DumbPointer<A> f{new A()};
f.moo(); // prints MOOMOO
f->moo(); // prints MOO
return 0;
}
重载 ->
运算符,使其看起来像一个指针,而实际上 DumbPointer
本身是一个对象。有关重载运算符的更多详细信息,请参见例如here。相关部分是:
If a user-defined operator-> is provided, the operator-> is called again on the value that it returns, recursively, until an operator-> is reached that returns a plain pointer. After that, built-in semantics are applied to that pointer.
PS:上面的例子是有保留的。例如DumbPointer<A> g = f;
会导致不好的事情发生。
我发现 James O. Coplien 在他的书中的解释非常清楚易懂 Advanced C++
。这是解释;
重载 operator->
与其他重载 C++ 运算符的工作方式不同。对于所有其他运算符,定义运算符实现的主体对从运算中得到的值 return 具有最终控制权。对于 operator->
,return 值是一个中间结果,然后应用 ->
的基本语义,产生一个结果。所以,
class B {
...
};
class A {
public:
B *operator->();
};
int main() {
A a;
... a->b ...
}
表示如下:
- 在对象
a
上调用A::operator->()
; - 在
x
类型B*
; 的临时 - 计算
x->b
,得出结果。
x
中捕获来自该调用的 return 值
这里,b
可以替换为 class B
的任何成员、数据或函数。如果 class B
重载 operator->
则再次应用上述三个步骤。