C++面向对象
本文最后更新于16 天前,其中的信息可能已经过时,如有错误请留言

类的作用域和类成员的访问(句柄)

成员运算符( . ) 作用域运算符(:: )

全局域 Global Scope

  • 定义:在 所有函数和类之外 定义的变量、函数或对象
  • 生命周期:整个程序运行期间存在(存储在静态存储区)
  • 访问权限:可以被 同一文件 的所有函数访问。通过 extern 声明,可被 其他文件 访问(全局变量跨文件共享)

局部域(Local Scope)

  • 定义:在 函数、代码块({})或类成员函数内 定义的变量或对象。
  • 生命周期:从定义点开始,到所在块结束时销毁(存储在栈上)
  • 访问权限 – 隐藏机制:仅在定义它的块内可见(如函数内部、if/for 块等)。同名局部变量会 隐藏(shadow) 外层变量(如全局变量)。如果还需要在内部使用被隐藏的同名全局变量的数据成员,可以通过在其名前加类名作用域运算符(:: ),全局变量前加作用域运算符来访问
int val = 100;

void badPractice() {
    int val = 50;  // 遮蔽全局变量
    std::cout << val;      // 输出: 50(局部变量)
    std::cout << ::val;    // 输出: 100(全局变量)
}

在类的作用域内,类的成员(数据和函数)可以被类的所有成员函数直接访问;

在类的作用域之外,,public类成员通过对象的句柄(handle)之一:对象,对象的引用,对象的指针来访问。

对象和对象的引用通过对象名 .成员名访问成员,对象的指针通过对象名->成员名访问成员、(每次通过对象的句柄调用成员函数时,编译器会插入一个隐式的this指针)。本质是 简化这个“解引用+访问”的过程的语法糖(syntactic sugar)ptr->member等价于 (*ptr).member

  • 为什么 -> 不能用于对象:因为 -> 是为指针设计的语法糖,对象没有指针的解引用步骤。如果强行用 obj->member,编译器会报错(除非类重载了 -> 运算符,如智能指针)。
  • 为什么引用为什么用 .引用是对象的别名,语法上完全等同于对象本身,因此用 .
  • struct 和 class 的成员访问规则完全一致
class Student {
public:
    int age;
    void study() { cout << "Studying..." << endl; }
};

int main() {
    Student s;       // 栈上的对象
    s.age = 20;      // 用 . 访问成员变量
    s.study();       // 用 . 调用成员函数

    Student& ref = s; // 引用
    ref.age = 21;     // 用 . 访问引用对象的成员
   
    Student* p = new Student(); // 堆上的对象(指针)
    p->age = 20;                // 用 -> 访问成员变量
    p->study();                 // 用 -> 调用成员函数

    //如果非要用 . 操作指针,可以先用 * 解引用指针,再使用 .,但代码会冗余,不推荐:
    Student* p = new Student();
    (*p).age = 20;  // 等价于 p->age = 20;
    (*p).study();   // 等价于 p->study();

    delete p; // 释放内存
    return 0;
}

如果成员是私有权限(private)或者被保护权限(protected)无法被访问,但存在两种情况私有权限(或被保护权限)的成员也可以访问:友元函数或友元类里

class MyClass {
private:
    int secret = 42;  // 私有成员

    // 声明友元函数
    friend void friendFunction(MyClass& obj);
    // 声明友元类
    friend class FriendClass;
};

// 友元函数定义
void friendFunction(MyClass& obj) {
    cout << "友元函数访问私有成员: " << obj.secret << endl;  // 合法
}

// 友元类定义
class FriendClass {
public:
    void showSecret(MyClass& obj) {
        cout << "友元类访问私有成员: " << obj.secret << endl;  // 合法
    }
};

类成员函数定义的本类对象

class Student {
private:
    int age;  // 私有成员

public:
    void setAge(int a) { age = a; }

    // 成员函数访问其他同类对象的私有成员
    void compareAge(const Student& other) {
        if (this->age > other.age) {  // 直接访问 other.age(合法)
            cout << "我比对方年长" << endl;
        } else {
            cout << "我比对方年轻" << endl;
        }
    }
};

类作为函数参数

函数参数类型如果是类,和基本数据类型一样,也有三种情况:值传递(类对象)、地址传递(对象指针)、引用传递(对象引用)。此外const也可修饰对象,所以共有6种函数参数的情况

  1. 类对象:void func(T t);
  2. 类对象:void func(const T t);
  3. 类对象指针:void func(T* pt);
  4. 类对象指针:void func(const T* pt);
  5. 类对象引用:void func(T&rt);
  6. 类对象引用:void func(const T& rt);

1和2实际传递进入函数的是实参的副本(调用拷贝构造函数),所以这两种情况在函数里不可能修改实参,且2对副本也不可能修改;

3和4用类对象指针作为参数,虽然对象指针传递进入函数也是这个对象指针的副本,但因为传递进入函数的是实参的地址,通过对象地址是能够修改实参对象的值;

4因为const修饰后是禁止通过地址修改实参的,所以4的效果是和1相似。而实际应用中通常更多使用1,因为简洁且安全

5和6用类对象引用作为参数,实际传递进入函数的就是实参本身,所以是能够直接修改对象的值。

其中5的效果和3相似,可以修改实参,但前面2.5节提过,使用引用要优于指针。

其中6因为const修饰后是禁止修改实参的,所以6的效果是和1相似。而实际应用中通常使用6更优,因为效率更优

学习笔记如有侵权,请提醒我,我会马上删除
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇