C++ 基础

🧮 C++ 操作符:::, ->, 和 .的含义与区别

为了快速把握要点,我们先通过一个表格来对比这三个操作符的核心差异:

特性::(作用域解析运算符)->(箭头操作符).(点操作符)
操作对象命名空间、类、枚举类指向对象实例的指针对象实例或引用
主要用途访问特定作用域内的成员(变量、函数、类型)通过指针访问其所指对象的成员直接访问对象实例或引用的成员
等价形式不适用(*ptr).member不适用
可否重载 (常用于智能指针、迭代器)
典型场景访问全局变量、静态成员、命名空间成员、在类外定义成员函数访问通过 new创建的堆对象、智能指针、迭代器访问栈上的局部对象、通过引用访问对象

接下来,我们详细了解一下每个操作符。

📋 1. 作用域解析运算符 ::

::运算符用于明确指定一个标识符(如变量、函数、类型)所在的作用域,从而避免名称冲突,并提高代码的可读性。

主要用途

  • 访问全局变量:当局部作用域有同名的变量时,用 ::可访问全局变量。

    c++
    int value = 100; // 全局变量
    void func() {
        int value = 50; // 局部变量
        std::cout << value;    // 输出 50 (局部变量)
        std::cout << ::value;  // 输出 100 (全局变量)
    }
  • 访问命名空间中的成员

    c++
    namespace MyNamespace {
    
    int x = 10;
    void foo() {}
    }
    
    int main() {
    	std::cout << MyNamespace::x; // 访问命名空间中的变量
        MyNamespace::foo();          // 访问命名空间中的函数
    }
  • 访问类的静态成员:静态成员属于类本身,而不是某个对象。

    c++
    class MyClass {
        public:
        	static int staticVar;
    };
    int MyClass::staticVar = 20; // 在类外定义并初始化静态成员
    int main() {
        std::cout << MyClass::staticVar; // 通过类名和::访问静态成员
    }
  • 在类外定义成员函数

    c++
    class MyClass {
        public:
        	void myFunction(); // 声明
    };
    // 在类外使用 :: 定义成员函数
    void MyClass::myFunction() {
        // 函数实现
    }
  • 访问嵌套类或枚举类

    c++
    class Outer {
        public:    class Inner { ... }; // 嵌套类
    };
    Outer::Inner innerObj; // 使用 :: 访问嵌套类

🔍 2. 箭头操作符 ->

->运算符用于通过指向对象实例的指针来访问该对象的成员(变量或函数)。它可以看作是先解引用指针,再使用点操作符的语法糖,即 ptr->member等价于 (*ptr).member

主要用途

  • 访问动态分配的对象成员:通过 new在堆上创建的对象,需要通过指针来访问。

    c++
    class MyClass {
        public:    int data;
        void print() {
            std::cout << data;
        }
    };
    MyClass* ptr = new MyClass(); // 动态分配,ptr是指针 ptr->data = 42;    // 通过 -> 访问成员变量 ptr->print();      // 通过 -> 调用成员函数 delete ptr;        // 记得释放内存
  • 访问智能指针指向的对象成员:如 std::unique_ptr, std::shared_ptr等,它们重载了 ->操作符,用法和原始指针类似。

    c++
    #include <memory>
    std::unique_ptr<MyClass> smartPtr = std::make_unique<MyClass>();
    smartPtr->data = 56; // 通过智能指针的 -> 访问成员
    smartPtr->print();
  • 访问迭代器指向的元素成员:标准库中的迭代器通常也重载了 ->,方便访问所指对象的成员。

    c++
    std::vector<MyClass> vec = ...;
    auto it = vec.begin(); // it是迭代器,类似于指针
    if (it != vec.end()) {
        std::cout << it->data; // 通过迭代器的 -> 访问元素的成员
    }

注意:使用 ->的前提是指针必须有效(例如不为 nullptr),否则可能导致未定义行为(如程序崩溃)。

🔬 3. 点操作符 .

.运算符用于通过对象实例对象的引用来直接访问其成员(变量或函数)。

主要用途

  • 访问局部(栈)对象成员:在函数内部创建的局部对象。

    c++
    class MyClass {
    public:
        int data;
        void print() { std::cout << data; }
    };
    MyClass obj; // 在栈上创建对象实例
    obj.data = 42;  // 通过 . 访问成员变量
    obj.print();    // 通过 . 调用成员函数
  • 通过引用访问对象成员

    c++
    MyClass obj;
    MyClass& ref = obj; // ref是obj的引用
    ref.data = 56;      // 通过引用和 . 访问成员
    ref.print();
  • 访问数组中的对象元素成员:数组中的元素是对象本身,而不是指针。

    c++
    MyClass arr[10]; // 数组元素是MyClass对象
    arr[0].data = 78; // 通过 . 访问数组元素的成员
    arr[0].print();

💡 常见错误与注意事项

  • 混淆 ->.:这是初学者常犯的错误。

    c++
    MyClass obj;
    MyClass* ptr = &obj;
    
    obj.data = 1;   // 正确,对象实例用 .
    ptr->data = 2;  // 正确,对象指针用 ->
    // ptr.data = 3; // 错误!指针不能直接用 . 访问成员
    // obj->data = 4; // 错误!对象实例不能直接用 -> 访问成员

    记住:对象或引用用 .,指针用 ->

  • ->之前检查指针有效性:对空指针使用 ->会导致运行时错误。

    c++
    MyClass* ptr = nullptr;
    // ptr->data = 10; // 危险!空指针解引用,通常导致程序崩溃
  • 理解 ::->/.的区别::用于基于作用域(类、命名空间) 的访问,而 ->.用于基于对象实例或指针的访问。例如,访问静态成员应该使用 Class::staticMember,而不是 obj.staticMember(尽管在某些编译器上后者可能通过,但前者才是正确且推荐的方式)。

💎 核心要点总结

  1. ::(作用域解析符):用于指明标识符所在的作用域,如命名空间、类、全局作用域。它操作的是作用域本身,而不是具体的对象实例。
  2. ->(箭头操作符):用于通过指向对象的指针来访问对象的成员。它要求左边是一个指针,并且可以重载。
  3. .(点操作符):用于通过对象实例或对象的引用来直接访问对象的成员。它要求左边是一个对象或引用,且不能被重载。

CC BY-NC-SA 4.0 乙巳 © 闻 · 斋