C++——名字遮蔽与类的作用域

发布于:2024-12-06 ⋅ 阅读:(29) ⋅ 点赞:(0)

C++ 类的作用域以及继承的特殊关系

名字遮蔽与类的作用域

如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,通过派生类对象或者在派生类的成员函数中使用该成员时,将使用派生类新增的成员,而不是基类的。
注意基类的成员函数和派生类的成员函数不会构成重载。如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数。
类是一种作用域,每个类都有它自己的作用域,在这个作用域之内定义成员。
在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象引用)来访问,静态成员可以通过对象访问,也可通过访问。
成员名前面类名域解析符可以访问对象的成员。
如果不存在继承关系,类名域解析符省略不写。
代码如下:

#include <iostream>

using namespace std;

class A
{
    public:
        int a_ = 123;
        int b_ = 456;
        void func() {cout << "调用 A 的 func() 函数" << endl;}
        void func(int n) {cout << "调用 A 的 func(int n) 函数" << endl;}
};

int main() {
    A a;
    cout << "a 的成员 a_ = " << a.a_ << endl;
    cout << "a 的成员 b_ = " << a.A::b_ << endl;
    a.func();
    a.A::func(1);

    return 0;
}

编译运行结果如下:

a 的成员 a_ = 123
a 的成员 b_ = 456
调用 A 的 func() 函数
调用 A 的 func(int n) 函数

当存在继承关系时,基类的作用域嵌套派生类的作用域中。如果成员派生类作用域中已经找到,就不会基类作用域中继续查找;如果没有找到,则继续基类作用域中查找。
如果在成员的前面加上类名域解析符,就可以直接使用该作用域的成员。
示例代码如下:

#include <iostream>

using namespace std;

class A // 基类
{
    public:
        int a_ = 111;
        void func() {cout << "调用基类 A 的 func() 函数" << endl;}
};

class B:public A // 子类
{
    public:
        int a_ = 222;
        void func() {cout << "调用子类 B 的 func() 函数" << endl;}
};

class C:public B // 孙类
{
    public:
        int a_ = 333;
        void func() {cout << "调用孙类 C 的 func() 函数" << endl;}
        void show() {
            cout << "C::a_ = " << C::a_ << endl;
            cout << "B::a_ = " << B::a_ << endl;
            cout << "A::a_ = " << A::a_ << endl;
        }
};


int main() {
    C c;
    c.show();
    c.C::func();
    c.B::func();
    c.A::func();

    return 0;
}

编译运行结果如下:

C::a_ = 333
B::a_ = 222
A::a_ = 111
调用孙类 C 的 func() 函数
调用子类 B 的 func() 函数
调用基类 A 的 func() 函数

继承的特殊关系

派生类和基类之间有一些特殊关系。

  1. 如果继承方式是公有的,派生类对象可以使用基类成员。
  2. 可以把派生类对象复制给基类对象(包括私有成员),但是,会舍弃非基类的成员。
#include <iostream>

using namespace std;

class A {
    public:
        int a_ = 0;
    private:
        int b_ = 0;
    public:
        void show() {cout << "A::show() a = " << a_ << ", b = " << b_ << endl; }
        void setb(int b) {b_ = b;}
};

class B : public A {
    public:
        int c_ = 0;
        void show() {
            cout << "B::show() a = " << a_ << " c = " << c_ << endl;
        }
};

int main() {
    A a;
    B b;

    b.a_ = 1;
    b.setb(2); // 通过公有成员函数访问修改基类私有成员
    b.c_ = 3;

    a.show();
    a = b; // 派生类对象b直接赋值给基类对象a
    a.show();

    return 0;
}

编译运行结果如下:

A::show() a = 0, b = 0
A::show() a = 1, b = 2

  1. 基类指针可以在不进行显式转换的情况下指向派生类对象。

示例代码如下:

#include <iostream>

using namespace std;

class A {
    public:
        int a_ = 0;
    private:
        int b_ = 0;
    public:
        void show() {cout << "A::show() a = " << a_ << ", b = " << b_ << endl; }
        void setb(int b) {b_ = b;}
};

class B : public A {
    public:
        int c_ = 0;
        void show() {
            cout << "B::show() a = " << a_ << " c = " << c_ << endl;
        }
};

int main() {
    B b;
    A *a = &b; // 声明类A的指针,让它指向b

    b.a_ = 1;
    b.setb(2); // 通过公有成员函数访问修改基类私有成员
    b.c_ = 3;
    b.show();
    
    // 在c++中,数据类型决定了操作数据的方法。
    // 现在a是A类的指针,在这个程序中,不管a指向谁,只会按A类的方法来操作数据。
    a->a_ = 111;
    a->setb(222);
    // a->c_ = 333; // 基类A没有c_这个成员 代码错误
    a->show();

    return 0;
}

编译运行的结果如下:

B::show() a = 1 c = 3
A::show() a = 111, b = 222

  1. 基类引用可以在不进行显式转换的情况下引用派生类对象。
  2. 基类指针或引用只能调用基类的方法,不能调用派生类的方法。
  3. 可以用派生类构造基类。
  4. 如果函数的形参是基类,实参可以用派生类。
  5. C++要求指针和引用类型与赋给的类型匹配,这一规则对继承来说是例外。但是,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针。

感谢浏览,一起学习!


网站公告

今日签到

点亮在社区的每一天
去签到

热门文章