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


243

从其他C来源的语言(如Java或C#)到C ++到来,这是起初很困惑的是C ++有三种方式来引用类的成员:a::ba.b,和a->b。什么时候使用这些运算符中的哪一个?

(注意:这本来是Stack Overflow的C ++ FAQ的一个条目。如果您想批评以这种形式提供FAQ的想法,那么在所有这些开始的meta上的张贴将是这样做的地方。该问题在C ++聊天室中进行监控,该问题最初是从FAQ想法开始的,所以提出这个想法的人很可能会读懂您的答案。)

Answers:


248

C ++用于访问类或类对象的成员的三个不同的运算符,即double Colon ::,dot .和arrow ->,用于始终定义良好的三个不同方案。认识到这一点,您可以立即知道了很多有关a,并b只要看一眼a::ba.ba->b分别,你看任何代码。

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

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

  3. a->b最初是的简写形式(*a).b。但是,->是唯一可以重载的成员访问运算符,因此,如果a是重载的类的对象operator->(通常此类是智能指针和迭代器),则其含义是类设计器所实现的。得出以下结论:使用a->b,如果a是指针,b则将是指针所a引用的对象的成员。但是,如果a是重载此运算符的类的对象,则将operator->()调用重载的运算符函数。


小字:

  • 在C ++中,声明为classstruct或的类型union被视为“类类型”。因此,以上提到了这三个方面。
  • 引用在语义上是对象的别名,因此我应该在#3上也添加“或引用指针”。但是,我认为这会比帮助更令人困惑,因为T*&很少使用对指针()的引用。
  • 点和箭头运算符可用于从对象引用静态类成员,即使它们不是对象的成员也是如此。(感谢Oli指出这一点!)

10
它应该有可能进行澄清,.并且->还可以通过对象来访问类静态,即使他们不是严格的“对象的成员”。
奥利弗·查尔斯沃思

@Oli:的确如此。我将其添加到小字体中,因为我认为它不常见且不重要,无法在正文中列出。
2011年

3
为了完整起见,可能值得指出的是operator*()也可以重载,并且没有什么可以迫使该重载与operator->()!保持一致。(我没有投票反对BTW,只是经过一连串重复重复才到达这里)
juanchopanza

@OliCharlesworth您知道C ++标准中指定的位置吗?

1
@juanchopanza:您无法获得 ->但是通过重载operator*和使用.。只有operator->重载才能做到这一点。
Ben Voigt 2014年

36

为sbi的观点3提出替代方案

a->b仅当a是指针时使用。它是的缩写,指向的对象(*a).bb成员a。C ++有两种指针,“常规”指针和智能指针。对于常规指针,例如A* a,编译器实现->。对于诸如此类的智能指针std::shared_ptr<A> a->是class的成员函数shared_ptr

基本原理:本常见问题解答的目标受众不是在编写智能指针。他们不需要知道->真的叫做operator->(),或者它是唯一可以重载的成员访问方法。


4
不管我是否同意,我都+1只是为了提供替代答案。
2011年

2
好吧,公平地说,对于->任何C ++程序员都应该很快会见到的标准迭代器,它也有很多重载,因此说仅将其用于指针可能会造成混淆。
Kiscsirke

@Kiscsirke“普通C ++程序员”不需要编写智能指针或迭代器类型,只需使用它们即可。“像指针一样的取消引用”适用于两者。
卡雷斯(Caleth)'17

0
#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)访问成员(属性和函数)使用指针运算符(->
*使用双冒号(::)从类本身访问静态成员函数,而无需将对象作为句柄。[ 注意:您也可以使用.->不建议从实例中调用静态成员函数]


@sbi好脾气哈,我知道那是某种重复。我只想举一个明确的例子来展示如何使用它们。我在哪里说->只能由在堆上分配的指针使用new?下面,第二项,我想我真的很清楚->是针对指针的。而且,在您减票之前,最好className::non_static_member_function()自己尝试使用c ++ 14。引用不是指针,因此可以使用.,我将在回答中更加清楚。
胡西溪

0

点运算符用于直接成员选择方案。

print(a.b)

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


箭头运算符用于间接成员选择方案。

print(a->b)

在这里,我们正在访问b的对象的成员a。它的缩写,(*a).b因此在这里a主要是指向对象的指针,并且b是该对象的成员。


Double Colon(Scope)运算符用于与名称空间相关的直接成员选择方案。

print(a::b)

在这里,我们访问的b是class / namespace的成员a。因此,主要a是class / namespace的并且ba

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.