C++零基础教程(父子间冲突)

简介: C++零基础教程(父子间冲突)

前言

本篇文章我们来讲解一下父子间的冲突问题,在C++中父子之前的冲突是非常常见的,这篇文章就带大家来学习一下父子间冲突。

一、子类中定义父类相同名字的变量

在子类中,可以定义和父类相同名字的变量,这会隐藏父类中相同名字的成员变量。这种情况下,子类中的成员变量会覆盖父类中相同名字的成员变量,导致父类的成员变量对子类不可见。

class Base {
public:
    int x;
    Base() : x(0) {}
};
class Derived : public Base {
public:
    int x;
    Derived() : x(1) {}
};
int main() {
    Derived obj;
    obj.x = 2;  // 子类的x变量
    obj.Base::x = 3;  // 父类的x变量
    return 0;
}

在上述例子中,子类 Derived 中定义了一个和父类 Base 相同名字的成员变量 x。当创建 Derived 类的对象时,会分别存在子类 Derived 的 x 和父类 Base 的 x。如果通过子类对象来访问 x,默认情况下访问的是子类的成员变量。可以使用作用域解析运算符 :: 来访问父类中的成员变量。

在 main 函数中,通过 obj.x 可以访问子类 Derived 中的 x,通过 obj.Base::x 可以访问父类 Base 中的 x。

总结起来,子类中可以定义与父类相同名字的成员变量,但会隐藏父类中的相同名字成员变量。通过作用域解析运算符可以访问父类成员变量。这种隐藏与访问方式可以让子类在需要时自定义相同名字的成员变量,而不会影响到父类的成员变量

二、子类中定义和父类相同的成员函数

函数重写(Override)是子类定义一个与父类中具有相同名称和参数列表的函数,并且具有相同的返回类型。通过函数重写,子类可以提供自己的实现,覆盖父类的函数实现。

#include <iostream>
class Base {
public:
    virtual void print() {
        std::cout << "Base类的成员函数print()" << std::endl;
    }
};
class Derived : public Base {
public:
    void print() override {
        std::cout << "Derived类的成员函数print()" << std::endl;
    }
};
int main() {
    Derived obj;
    obj.print();  // 调用子类的print()函数
    obj.Base::print();  // 通过作用域解析运算符调用父类的print()函数
    return 0;
}

子类 Derived 重写(Override)了父类 Base 的成员函数 print(),并使用 override 关键字进行标识。当通过子类对象调用 print() 函数时,将会使用子类 Derived 中重写的函数版本。

Derived类的成员函数print()
Base类的成员函数print()

从输出结果可以看出,子类对象调用的 print() 函数是子类 Derived 中重写的版本。使用作用域解析运算符 obj.Base::print() 可以显式地调用父类 Base 中的 print() 函数。

三、子类可以重载父类中的函数吗

在C++中,重载(Overload)是指在同一个类中定义具有相同名称但不同参数列表的多个函数。这意味着在父类和子类之间无法进行函数重载,因为子类中定义的函数必须具有与父类中相同的名称和参数列表,否则将被视为一个新的函数,而不是重载。

函数重载必然发生在同一个作用域中

四、父子之间的复制兼容性

在C++中,父类和子类之间存在赋值兼容性,这是因为子类对象可以被视为父类对象的一种特殊形式。

当将子类对象赋值给父类对象时,会发生对象切片(Object Slicing)。对象切片是指将子类对象赋值给父类对象时,只会保留父类对象中与子类对象相对应的部分,而子类对象自己的额外成员和方法会被丢失。

下面是一个示例来说明父类和子类之间的赋值兼容性:

#include <iostream>
class Base {
public:
    int x;
    void print() {
        std::cout << "Base类的成员函数print()" << std::endl;
    }
};
class Derived : public Base {
public:
    int y;
    void print() {
        std::cout << "Derived类的成员函数print()" << std::endl;
    }
};
int main() {
    Derived obj1;
    obj1.x = 10;
    obj1.y = 20;
    Base obj2 = obj1; // 子类对象赋值给父类对象
    std::cout << obj2.x << std::endl; // 输出:10
    // std::cout << obj2.y << std::endl; // 错误:父类对象没有成员变量y
    obj2.print(); // 输出:Base类的成员函数print()
    return 0;
}

在上述示例中,子类 Derived 继承自父类 Base。首先创建了一个 Derived 类的对象 obj1,并给其成员变量 x 和 y 分别赋值。然后通过将 obj1 赋值给父类对象 obj2,发生了对象切片。在 obj2 中只保留了与父类 Base 相关的部分,子类 Derived 特有的成员变量 y 被丢失。

在输出中,obj2.x 的值为 10,说明父类对象 obj2 成功获取了子类对象 obj1 的 x 成员变量的值。而访问 obj2.y 会导致编译错误,因为父类对象没有成员变量 y。最后,通过调用 obj2.print(),实际上是调用了父类 Base 中的 print() 函数,而不是子类 Derived 中的 print() 函数。

总结起来,父类和子类之间存在赋值兼容性,可以将子类对象赋值给父类对象。但在进行赋值时会发生对象切片,导致丢失子类对象的特有成员和方法。

注意:

在C++中,父类对象不能直接赋值给子类对象。这是由于子类对象可能包含父类对象无法拥有的额外成员和方法,因此将父类对象赋值给子类对象可能导致数据的丢失和类型不匹配问题。

总结

本篇文章就讲解到这里,下篇文章讲解什么是多态。


相关文章
|
5天前
|
存储 测试技术 编译器
面向 C++ 的现代 CMake 教程(三)(5)
面向 C++ 的现代 CMake 教程(三)
23 1
|
2天前
|
C++ 存储 索引
面向 C++ 的现代 CMake 教程(一)(5)
面向 C++ 的现代 CMake 教程(一)
33 0
|
2天前
|
缓存 存储 C++
面向 C++ 的现代 CMake 教程(一)(4)
面向 C++ 的现代 CMake 教程(一)
32 0
|
2天前
|
C++ 缓存 存储
面向 C++ 的现代 CMake 教程(一)(3)
面向 C++ 的现代 CMake 教程(一)
27 0
|
2天前
|
缓存 C++ Windows
面向 C++ 的现代 CMake 教程(一)(2)
面向 C++ 的现代 CMake 教程(一)
40 0
|
2天前
|
C++ 容器 Docker
面向 C++ 的现代 CMake 教程(一)(1)
面向 C++ 的现代 CMake 教程(一)
47 0
|
2天前
|
存储 算法 C++
面向 C++ 的现代 CMake 教程(五)(5)
面向 C++ 的现代 CMake 教程(五)
20 0
|
2天前
|
C++ 存储 JSON
面向 C++ 的现代 CMake 教程(五)(4)
面向 C++ 的现代 CMake 教程(五)
28 0
|
2天前
|
C++ 存储 测试技术
面向 C++ 的现代 CMake 教程(五)(3)
面向 C++ 的现代 CMake 教程(五)
27 0
|
2天前
|
C++ 存储 测试技术
面向 C++ 的现代 CMake 教程(五)(2)
面向 C++ 的现代 CMake 教程(五)
28 0