在c++中什么时候使用点、箭头或双冒号来引用类的成员?

从其他C语言(如Java或c#)到c++, c++有三种方法来引用类的成员:a::ba.ba->b,这一开始是非常令人困惑的。什么时候使用这些操作符中的哪一个?

_(注意:这是[Stack Overflow's c++ FAQ](https://stackoverflow.com/questions/tagged/c++-faq)的一个条目。如果你想批评以这种形式提供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的想法最初就是在那里开始的,所以你的答案很可能会被想出这个想法的人读到
95150 次浏览

c++用于访问类或类对象成员的三个不同操作符,即双冒号::、点.和箭头->,用于三个始终定义良好的不同场景。知道了这一点,你就可以通过在你看到的任何代码中分别查看a::ba.ba->b,立即了解很多关于ab的信息。

  1. a::b只在b是类(或命名空间)a的成员时使用。也就是说,在这种情况下a将始终是类(或命名空间)的名称。

  2. a.b仅在ba对象(或对象引用)的成员时使用。因此对于a.ba将始终是类的一个实际对象(或对对象的引用)。

  3. a->b最初是(*a).b的缩写符号。然而,->是唯一可以重载的成员访问操作符,因此如果a是重载operator->的类的对象(常见的此类类型是智能指针和迭代器),则意义取决于类设计器实现了什么。总结:对于a->b,如果a是一个指针,那么b将是指针a所指向的对象的成员。但是,如果a是重载此操作符的类的对象,则重载操作符函数(*a).b0将被调用。


细则:

  • 在c++中,声明为__ABC0、__ABC1或union的类型被认为是“类类型”。所以上面提到了这三个。
  • 从语义上讲,引用是对象的别名,所以我应该添加“或引用指针”到#3。然而,我认为这将更令人困惑而不是有用,因为指针(T*&)的引用很少被使用
  • 点和箭头操作符可用于引用对象的静态类成员,即使它们不是对象的成员。(感谢Oli指出这一点!)

为sbi的第3点提出了替代方案

a->b仅在a是指针时使用。它是(*a).b的缩写,(*a).ba所指向对象的b成员。c++有两种指针,“常规”指针和智能指针。对于像A* a这样的常规指针,编译器实现了->。对于诸如std::shared_ptr<A> a这样的智能指针,->是类shared_ptr的成员函数。

基本原理:本FAQ的目标读者并不是在编写智能指针。他们不需要知道->实际上被称为operator->(),或者它是唯一可以重载的成员访问方法。

#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创建)的指针访问成员(属性和函数) *使用双冒号(::)从类本身访问静态成员函数,而没有对象作为句柄。[注意:你也可以用.->实例调用静态成员函数,这是不推荐的]

点运算符在直接成员选择场景。中使用

print(a.b)

这里,我们正在访问b,它是对象a的直接成员。因此,首先,a是一个对象,而ba的成员(函数/变量等)。


箭头操作符在间接成员选择场景。中使用

print(a->b)

在这里,我们正在访问b,它是对象的成员,由a指向。它是(*a).b的缩写,所以在这里,a主要是指向对象的指针,而b是该对象的成员。


双冒号(作用域)操作符在与名称空间相关的直接成员选择场景。中使用

print(a::b)

这里,我们正在访问b,它是类/命名空间a的成员。因此,a主要是一个类/命名空间,而ba的成员(函数/变量等)。