欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【C++深度解析】33、子类定义父类同名的函数或变量

程序员文章站 2022-05-29 18:17:43
...

1 子类定义父类同名成员

1.1 子类定义父类同名变量

子类中可以定义父类的同名成员

  • 子类中的成员将隐藏父类中的同名成员
  • 父类中的同名成员依然存在于子类中,需要用作用域分辨符(::)访问父类同名成员
// 33-1.cpp
#include<iostream>
using namespace std;
namespace A
{
    int g = 0;
}
namespace B
{
    int g = 1;
}
class Parent
{
public:
    int mi;
    Parent()
    {
        cout << "Parent() : " << "&mi = " << &mi << endl;
    }
};
class Child : public Parent
{
public:
    int mi;
    Child()
    {
        cout << "Child() : " << "&mi" << &mi << endl;
    }
};
int main()
{
    Child c;
    c.mi = 100;
    c.Parent::mi = 1000;
    cout << "&c.mi = " << &c.mi << endl;
    cout << "c.mi = " << c.mi << endl;

    cout << "&c.Parent::mi = " << &c.Parent::mi << endl;
    cout << "c.Parent::mi = " << c.Parent::mi << endl;
    return 0;
}

类 Parent 和类 Child 中都定义了变量 mi,Child 类中隐藏了父类中的 mi 变量,我们如果想访问父类中的变量 mi,需要使用作用域分辨符(::)来访问。

父类与子类中同名变量的作用域不同,不会发生冲突,就像命名空间 A 和 B 中都有变量 g,也不会冲突,因为他们的作用域不同。

编译运行

$ g++ 33-1.cpp -o 33-1
$ ./33-1
Parent() : &mi = 0x7ffe80cc1340
Child() : &mi0x7ffe80cc1344
&c.mi = 0x7ffe80cc1344
c.mi = 100
&c.Parent::mi = 0x7ffe80cc1340
c.Parent::mi = 1000

可以看到 c.mi 访问的是子类的成员,c.Parent::mi 访问的是父类的成员。

1.2 子类定义父类同名函数

  • 子类中的函数将隐藏父类的同名函数
  • 子类无法重载父类中的成员变量
  • 使用作用域分辨符访问父类中的同名函数
  • 子类可以定义父类中完全相同的成员函数
// 33-2.cpp
#include<iostream>
using namespace std;
class Parent
{
public:
    int mi;
    void add(int a)
    {
        mi += a;
    }
    void add(int a, int b)
    {
        mi += (a + b);
    }
};
class Child : public Parent
{
public:
    int mi;
    void add(int a, int b, int c)
    {
        mi += (a + b + c);
    }
};
int main()
{
    Child c;
    c.mi = 10;
    c.Parent::mi = 100;
    c.Parent::add(1);
    c.Parent::add(2, 3);
    c.add(4, 5, 6);
    cout << "c.mi = " << c.mi << endl;
    cout << "c.Parent::mi = " << c.Parent::mi << endl;
    return 0;
}

Parent 和 Child 中都定义了 add 函数,Child 中的 add 函数会隐藏 Parent 中的 add 函数,他们不构成重载关系

编译运行:

$ g++ 33-2.cpp -o 33-2
$ ./33-2
c.mi = 25
c.Parent::mi = 106

Parent 中的 mi 被加上 1+2+3,等于106;Child 中的 mi 被加上 4 + 5 + 6,等于 25。

如果将第 31,32 行的代码改为 c.add(1); c.add(2, 3); 重新编译,会出错,因为子类掩盖父类中的同名函数,Child 对象找不到对应的函数。

如图,编译器提示,只能找到函数 void add(int a, int b, int c),找不到 void add(int a) 和 void add(int a, int b)
【C++深度解析】33、子类定义父类同名的函数或变量

2 父子间的赋值

2.1 父子间的赋值兼容

子类对象可以当父类对象使用

  • 子类对象可以直接赋值给父类对象
  • 子类对象可以直接初始化父类对象
  • 父类指针可以直接指向子类对象
  • 父类引用可以直接引用子类对象

当使用父类的指针(引用)指向子类对象时,子类对象退化为父类对象,只能访问父类中的成员,可以直接访问被子类覆盖的同名成员。

编程实验:子类对象的兼容性

// 33-3.cpp
#include<iostream>
using namespace std;
class Parent
{
public:
    int mi;
    void add(int a)
    {
        mi += a;
    }
    void add(int a, int b)
    {
        mi += (a + b);
    }
};
class Child : public Parent
{
public:
    int mv;
    void add(int a, int b, int c)
    {
        mv += (a + b + c);
    }
};
int main()
{
    Parent p;
    Child c;

    p = c;				// 子类对象赋值给父类对象
    Parent p1(c);		// 子类对象初始化父类对象
    Parent& rp = c;		// 父类引用引用子类对象
    Parent* pp = &c;	// 父类指针指向子类对象
    
    rp.mi = 100;
    rp.add(1);
    rp.add(2, 3);

    // pp->mv = 1000;
    // pp->add(4, 5, 6);
    return 0;
}

当使用父类的指针(引用)指向子类对象时,子类对象退化为父类对象,第 40,41 行,pp 已经退化为父类对象,无法访问子类成员。rp 也同样退化为父类对象,可直接访问父类成员,上述代码可编译运行。

2.2 函数重写

函数重写:子类中重定义父类中已经存在的成员函数

当函数重写碰见父子间的赋值兼容会发生什么呢,下面进行编程实验进行验证。

// 33-4.cpp
#include<iostream>
using namespace std;
class Parent
{
public:
    int mi;
    void add(int a)
    {
        mi += a;
    }
    void add(int a, int b)
    {
        mi += (a + b);
    }
    void print()
    {
        cout << "I am Parent" << endl;
    }

};
class Child : public Parent
{
public:
    int mv;
    int add(int a, int b, int c)
    {
        mv += (a + b + c);
    }
    void print()
    {
        cout << "I am Child" << endl;
    }
};
void how_to_print(Parent* p)
{
    p->print();
}
int main()
{
    Parent p;
    Child c;
    how_to_print(&p);
    how_to_print(&c);
    return 0;
}

父子类中都定义了 void print() 函数,将父类对象作为参数传入函数 void how_to_print(Parent* p),相当于父类指针分别指向父类对象和子类对象。父类指针指向父类对象,自然调用父类成员函数;父类指针指向子类对象,时,子类对象退化为父类对象,调用的还是父类成员函数,两次打印的都是 I am Parent,真的是这样吗?

编译运行:

$ g++ 33-4.cpp -o 33-4
$ ./33-4
I am Parent
I am Parent

和我们分析的一样

3 小结

1、子类可以定义父类的同名成员,子类掩盖父类同名成员
2、子类和父类的函数不构成重载关系
3、使用作用域分辨符访问父类同名成员
4、子类对象可以当作父类对象使用(赋值兼容)
5、父类指针、引用可以指向子类对象(退化为父类对象)
6、子类可以重写父类成员函数

相关标签: C++深度解析