C++中"::"、"."和"->"有什么区别?

98
我创建了一个名为 Kwadrat 的类。该类有三个 int 类型的字段。我的开发环境建议我通过使用 ::-> 运算符从创建的 Kwadrat 对象中访问这些字段。我尝试了这两种运算符,并发现 -> 运算符能够成功访问对象字段中的数据,但是对于 . 运算符就不行了。
我还发现,. 运算符也可以访问类成员。我很困惑,不明白为什么有三个运算符来访问对象成员和/或方法。请问有谁能解释一下这三个运算符之间的区别吗?

          1. ->

          2. ::

          3. .




    #include <iostream>

    using namespace std;

    class Kwadrat{

    public: 
        int val1,
            val2,
            val3;

        Kwadrat(int val1, int val2, int val3)
        {
            this->val1 = val1; // Working
            this.val2 = val2;  // Doesn't Work!
            this::val3 = val3; // Doesn't Work!
        }
    };


    int main()
    {
        Kwadrat* kwadrat = new Kwadrat(1,2,3);

        cout<<kwadrat->val1<<endl;
        cout<<kwadrat->val2<<endl;
        cout<<kwadrat->val3<<endl;

        return 0;
    }



1
请参见 https://dev59.com/_HM_5IYBdhLWcg3ww2Gb#1238639 - Tadeusz Kopec for Ukraine
8个回答

180

1. 使用指向对象的指针通过->访问对象成员变量和方法。

Foo *foo = new Foo();
foo->member_var = 10;
foo->member_func();

2. 通过对象 实例(instance) 访问对象成员变量和方法的 .

Foo foo;
foo.member_var = 10;
foo.member_func();

3.:: 用于访问class/structnamespace的静态变量和方法。它也可以用于从另一个作用域访问变量和函数(实际上,类、结构体和命名空间在这种情况下都是作用域)。

int some_val = Foo::static_var;
Foo::static_method();
int max_int = std::numeric_limits<int>::max();

1
我知道,我只是在暗示你可能想把它添加到2中。 - ltjax
1
你可以引用它们中的任何一个。引用始终保持相同的类型。这实际上是一个不同的主题。 - user1454661
2
该列表并非详尽无遗,也不是百分之百正确的。作用域操作符可以在更多情况下用于限定实体,即使它们不是静态的:void derived::f() { base::f(); } 甚至与其他两个操作符结合使用:obj.base::f(),而访问操作符可用于访问静态变量:struct test { static const int i = 1; }; test t; int j = t.i;... - David Rodríguez - dribeas
2
@Andrew:我们的个人喜好与运算符无关。在这种语言中有很多我不喜欢的东西,但它们仍然存在... - David Rodríguez - dribeas
我会添加一段简短说明,即pointer->doSomething()执行内联解引用,实际上与(&pointer).doSomething()相同;只是语法糖。 - Jax
显示剩余4条评论

30

在C++中,您可以使用不同的操作符访问字段或方法,具体取决于其类型:

  • ClassName::FieldName:类的公共静态字段和方法
  • ClassInstance.FieldName:通过类引用访问公共字段(或方法)
  • ClassPointer->FieldName:解除类指针引用以访问公共字段(或方法)

请注意,::应该与类名一起使用,而不是类实例,因为静态字段或方法适用于类的所有实例。

class AClass{
public:
static int static_field;
int instance_field;

static void static_method();
void method();
};

然后你可以这样访问:

AClass instance;
AClass *pointer = new AClass();

instance.instance_field; //access instance_field through a reference to AClass
instance.method();

pointer->instance_field; //access instance_field through a pointer to AClass
pointer->method();

AClass::static_field;  
AClass::static_method();

但我不能通过AClass :: static_field获得访问权限;将其设置为0; - Yoda
我喜欢你评论中的Unicode字符,@Yoda :) - Gewure

19

::是作用域操作符,.是访问操作符(我忘记实际名称是什么了?),->则是解引用箭头。

:: - 作用是限定函数的作用域。也就是说,它让编译器知道该函数属于哪个类,并确定如何调用它。如果你要使用这个操作符来调用一个函数,那么该函数应该是一个静态函数。

. - 可以访问已创建对象的成员函数。例如,Foo x; x.bar()在类型为Foo的实例化对象x上调用方法bar()。你还可以使用它来访问公共类变量。

-> - 与.基本相同,但作用于指针类型。它解引用指针,然后调用.。使用它等价于(*ptr).method()


我认为 . 运算符字面上被称为“点运算符”。 - Ascent

8

您有一个指向对象的指针。因此,您需要访问指针所指向的对象的字段。要取消引用指针,您可以使用*,要访问字段,您可以使用.,因此您可以使用:

cout << (*kwadrat).val1;

请注意,括号是必要的。这个操作很常见,早在很久以前(当C语言还年轻时),他们决定创建一个“简写”方法来执行它:

cout << kwadrat->val1;

这些被定义为相同的。如您所见,-> 基本上只是将 *. 结合成一个单独的操作符。如果您直接处理对象或对对象的引用,您可以在不首先对指针进行解引用的情况下使用 .

Kwadrat kwadrat2(2,3,4);

cout << kwadrat2.val1;

:: 是作用域解析运算符。当您只需要限定名称,但根本没有涉及到单个对象时,可以使用它。这主要用于访问静态数据成员:

struct something { 
    static int x; // this only declares `something::x`. Often found in a header
};

int something::x;  // this defines `something::x`. Usually in .cpp/.cc/.C file.

在这种情况下,由于xstatic的,它不与something的任何特定实例相关联。事实上,即使没有创建该类型对象的任何实例,它也将存在。在这种情况下,我们可以使用作用域解析运算符来访问它:

something::x = 10;

std::cout << something::x;

需要注意的是,也允许将静态成员访问为特定对象的成员:

something s;

s.x = 1;

至少在我记忆中,早期的C++不允许这样做,但意思很明确,所以他们决定允许它。


你帮了我很多,但我不明白为什么这个代码不起作用。class Something{ public: static int i; }; Something::i = 0; cout<<Something::i<<endl; - Yoda
1
@RobertKilar:这里声明了 Something::i,但并没有定义它,所以你需要一个单独的定义,例如我在答案中展示的 int Something::i;。否则,代码将会编译但无法链接,因为 Something::i 将成为一个“未解决的外部/未定义引用”。 - Jerry Coffin

7
三个运算符具有相关但不同的含义,尽管IDE中有误导性的注释。
:: 运算符被称为作用域解析运算符,用于从命名空间或类转到其成员之一。
. 和 -> 运算符用于访问对象实例的成员,在创建对象实例后才会发挥作用。如果您有一个实际对象(或对对象的引用,在声明类型时使用 &),则使用 .,如果您有一个指向对象的指针(在声明类型时使用 *),则使用 -> 。
此对象始终是当前实例的指针,因此 -> 运算符是唯一有效的运算符。
示例:
// In a header file
namespace Namespace {
    class Class {
        private:
            int x;
        public:
            Class() : x(4) {}
            void incrementX();
    };
}

// In an implementation file
namespace Namespace {
    void Class::incrementX() {    // Using scope resolution to get to the class member when we aren't using an instance
        ++(this->x);              // this is a pointer, so using ->. Equivalent to ++((*this).x)
    }
}

// In a separate file lies your main method
int main() {
    Namespace::Class myInstance;   // instantiates an instance. Note the scope resolution
    Namespace::Class *myPointer = new Namespace::Class;
    myInstance.incrementX();       // Calling a function on an object instance.
    myPointer->incrementX();       // Calling a function on an object pointer.
    (*myPointer).incrementX();     // Calling a function on an object pointer by dereferencing first

    return 0;
}

3

-> 是指向类实例的指针

. 是用于类实例本身

:: 是用于类名 - 例如在使用静态成员时


2
'::'是用于静态成员的。

1

其他人已经回答了不同的语法,但请注意,在进行couts时,您只使用->

int main()
{
    Kwadrat* kwadrat = new Kwadrat(1,2,3);
    cout<<kwadrat->val1<<endl;
    cout<<kwadrat->val2<<endl;
    cout<<kwadrat->val3<<endl;
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接