从其他 C 派生语言(如 Java 或 C#)到 C++,C++ 具有三种引用类成员的方式一开始非常令人困惑:a::b
、a.b
和 a->b
。我什么时候使用这些运算符中的哪一个?
_(注意:这是 [Stack Overflow 的 C++ 常见问题解答](https://stackoverflow.com/questions/tagged/c++-faq) 的一个条目。如果您想批评以这种形式提供常见问题解答的想法,然后[开始这一切的 meta 上的帖子](https://meta.stackexchange.com/questions/68647/setting-up-a-faq-for-the-c-tag)将是这样做的地方. 该问题的答案在 [C++ 聊天室](https://chat.stackoverflow.com/rooms/10/c-lounge) 中进行监控,FAQ 想法最初就是从那里开始的,所以你的答案很可能让提出这个想法的人阅读。)_
C++ 用于访问类或类对象成员的三个不同运算符,即双冒号 ::
、点 .
和箭头 ->
,用于三种不同的场景,这些场景总是定义明确的.知道这一点后,您只需分别查看您查看的任何代码中的 a::b
、a.b
或 a->b
,即可立即了解很多关于 a
和 b
的信息。
a::b 仅在 b 是类(或命名空间)a 的成员时使用。也就是说,在这种情况下,a 将始终是类(或命名空间)的名称。仅当 b 是对象(或对对象的引用)a 的成员时才使用 ab。因此对于 ab,a 将始终是类的实际对象(或对对象的引用)。 a->b 最初是 (*a).b 的简写符号。但是,-> 是唯一可以重载的成员访问运算符,因此如果 a 是重载 operator-> 的类的对象(常见的此类类型是智能指针和迭代器),那么其含义就是类设计者实施的。总结:对于 a->b,如果 a 是指针,b 将是指针 a 所指对象的成员。但是,如果 a 是重载此运算符的类的对象,则将调用重载的运算符函数 operator->()。
小字:
在 C++ 中,声明为类、结构或联合的类型被视为“类类型”。所以上面提到的都是他们三个。
引用在语义上是对象的别名,所以我也应该在#3 中添加“或对指针的引用”。但是,我认为这会比有用更令人困惑,因为很少使用对指针 (T*&) 的引用。
点和箭头运算符可用于引用对象中的静态类成员,即使它们不是对象的成员。 (感谢奥利指出这一点!)
为 sbi 的第 3 点提出替代方案
仅当 a
是指针时才使用 a->b
。它是 (*a).b
的简写,a
指向的对象的 b
成员。 C++ 有两种指针,“常规”指针和智能指针。对于 A* a
等常规指针,编译器实现 ->
。对于 std::shared_ptr<A> a
等智能指针,->
是类 shared_ptr
的成员函数。
理由:本常见问题解答的目标受众不是编写智能指针。他们不需要知道 ->
真的称为 operator->()
,或者它是唯一可以重载的成员访问方法。
+1
只是为了提供一个替代答案。
->
对于任何 C++ 程序员都应该很快遇到的标准迭代器也是重载的,所以说它仅用于指针可能会令人困惑。
点运算符用于直接成员选择场景。
print(a.b)
在这里,我们正在访问 b
,它是对象 a
的直接成员。因此,首先,a
是一个对象,而 b
是 a
的成员(函数/变量等)。
箭头运算符用于间接成员选择场景。
print(a->b)
在这里,我们正在访问 b
,它是对象的成员,由 a
指向。它是 (*a).b
的简写,因此在这里,a
主要是指向对象的指针,而 b
是该对象的成员。
双冒号(Scope)运算符用于命名空间相关的直接成员选择场景。
print(a::b)
在这里,我们访问的是 b
,它是类/命名空间 a
的成员。因此,首先,a
是类/命名空间,b
是 a
的成员(函数/变量等) .
#include <iostream>
#include <string>
using namespace std;
class Human {
private:
int age;
public:
string name;
Human(int humanAge, string humanName)
: age(humanAge), name(std::move(humanName)) {}
void DoSomething() {
cout << age << endl;
}
static void DisplayAge(const Human& person) {
cout << person.age << endl;
}
// ...
};
int main() {
// Usage of Dot(.)
Human firstMan(13, "Jim"); // firstMan is an instance of class Human
cout << firstMan.name << endl; // accessing member attributes
firstMan.DoSomething(); // accessing member functions
// Usage of Pointer Operator (->)
Human* secondMan = new Human(24, "Tom");
cout << secondMan->name << endl; // accessing member attributes
secondMan->DoSomething(); // accessing member functions
cout << (*secondMan).name << endl; // accessing member attributes
(*secondMan).DoSomething(); // accessing member functions
// Usage of Double Colon (::)
Human::DisplayAge(firstMan);
firstMan.DisplayAge(firstMan); // ok but not recommended
secondMan->DisplayAge(firstMan); // ok but not recommended
delete(secondMan);
return 0;
}
从上面的代码示例中,我们看到:
* 使用点运算符 (.
) 从实例(或对象)访问成员(属性和函数)
* 从一个实例(或对象)访问成员(属性和函数)使用指针运算符 (->
) 指向对象(或由 new
创建)的指针
* 从类本身访问静态成员函数,而无需使用双冒号 (::
) 作为句柄的对象。 [注意:您也可以使用 .
或 ->
从实例调用静态成员函数,不推荐使用]
->
只能由 new
在堆上分配的指针使用?下面,第二项,我想我真的很清楚->
是指针。在你投反对票之前,你最好自己用 c++14 尝试 className::non_static_member_function()
。引用不是指针,所以它可以使用.
,我会在我的回答中说得更清楚。
.
和->
也可以用于通过对象访问类静态,即使它们不是严格的“对象的成员”。operator*()
也可以重载,并且没有任何东西强制重载与operator->()
一致! (顺便说一句,我没有投反对票,只是通过一长串重复来到这里)operator*
和使用.
来获得->
的链接行为。只有operator->
重载可以做到这一点。