QT学习记录(009):公共函数、私有函数和被保护函数区别,及虚函数的作用

1 公共函数、私有函数和被保护函数区别

1.1 公共函数(Public Functions)

        a)公共函数是类对外提供的接口,可以被类的对象和外部代码直接调用。

        b)公共函数通常包含类的主要功能,用于供外部使用。

class MyClass {
public:
    void publicFunction();
};

1.2 私有函数(Private Functions)

        a)私有函数是类的内部实现细节,只能被类的其他成员函数调用,而不能被外部代码直接访问。

        b)私有函数通常用于实现类的内部逻辑,不对外公开。

class MyClass {
private:
    void privateFunction();
};

1.3 被保护函数(Protected Functions)

        a)被保护函数类似于私有函数,不过它可以被派生类(子类)访问

        b)被保护函数通常用于提供给派生类使用的一些功能,而不直接暴露给外部。

class MyBaseClass {
protected:
    void protectedFunction();
};

class MyDerivedClass : public MyBaseClass {
    // 可以访问 MyBaseClass 的 protectedFunction()
};

2 虚函数的作用

2.1 虚函数(Virtual Functions)的作用

        主要涉及到类的多态性和动态绑定。在基类中声明为虚函数的函数,可以在派生类中进行重写。虚函数的主要作用有:

2.1.1 实现动态多态性(Dynamic Polymorphism)

        允许通过基类指针或引用调用派生类的函数,实现运行时的动态绑定。

class MyBaseClass {
public:
    virtual void virtualFunction();
};

class MyDerivedClass : public MyBaseClass {
public:
    void virtualFunction() override;
};

MyBaseClass* ptr = new MyDerivedClass;
ptr->virtualFunction(); // 调用 MyDerivedClass 的实现

2.1.2 允许派生类重写基类的函数

         派生类可以通过 override 关键字重写基类中的虚函数。

class MyBaseClass {
public:
    virtual void virtualFunction();
};

class MyDerivedClass : public MyBaseClass {
public:
    void virtualFunction() override; // 重写基类的虚函数
};

        虚函数的使用使得程序更容易进行扩展和维护,同时提供了更灵活的设计。在Qt中,特别是在涉及到信号和槽的机制时,虚函数的使用也是非常常见的。

2.2 override关键词的使用

        override 是在C++11标准中引入的一个关键字,用于显式地告诉编译器,某个函数是在派生类中对基类中的虚函数进行重写(override)。

        具体来说,override 关键字在函数声明的末尾使用,用于指明派生类的成员函数是对基类中的虚函数的重写,这样可以在编译时进行检查,确保重写关系的正确性。

class Base {
public:
    virtual void print() const;
};

class Derived : public Base {
public:
    void print() const override; // 使用 override 声明对基类虚函数的重写
};

        在上面的例子中,Derived 类中的 print 函数使用了 override 关键字,表明它是对 Base 类中的虚函数 print 的重写。如果在 Derived 中没有找到与 Base 中的虚函数 print 匹配的函数,或者签名不匹配,编译器会发出错误。这有助于避免因为拼写错误、参数类型不匹配等原因导致的意外问题。

        总的来说,override 关键字在C++中是一种良好的编程实践,能够帮助开发者更容易地识别和解决与虚函数重写相关的问题。

2.3 虚函数重写,而非重新声明一个新的函数原因

        重写虚函数而不是新声明一个函数的主要原因涉及到面向对象编程中的多态性和动态绑定的概念。

2.3.1 实现运行时多态性

        虚函数和重写允许在运行时动态绑定。当基类指针或引用指向派生类对象时(通过基类的指针或引用调用派生类中的成员函数,动态绑定),通过虚函数调用可以实现对应派生类版本的函数。这使得程序更加灵活,能够在运行时根据对象的实际类型调用适当的函数。

class Shape {
public:
    virtual void draw() const;
};

class Circle : public Shape {
public:
    void draw() const override;
};

void drawShape(const Shape& shape) {
    shape.draw(); // 根据对象的实际类型调用对应的 draw() 函数
}

2.3.2 扩展基类功能

        如果基类中定义了虚函数,派生类可以选择是否重写这个函数。通过重写,派生类可以保留基类的行为并添加自己的实现

class Animal {
public:
    virtual void makeSound() const;
};

class Dog : public Animal {
public:
    void makeSound() const override {
        // 添加狗的特有声音
    }
};

        这样的设计使得代码更易于扩展和维护,可以在不影响现有代码的情况下增加新的派生类。

2.3.3 使用基类指针处理派生类对象

        虚函数使得可以使用基类指针或引用来处理派生类对象,而不需要知道对象的实际类型。

Shape* shapePtr = new Circle;
shapePtr->draw(); // 调用 Circle 类的 draw() 函数

        总的来说,重写虚函数的目的是为了支持多态性,使得程序更具有扩展性和适应性。如果你只是想写一个新的函数而不是扩展或替代基类的函数,可能并不需要使用虚函数机制。选择是否使用虚函数取决于设计的需求和目标。

2.4 const Shape& shape中,shape表示对 Shape 类型的常量引用

        在函数 drawShape 中,const Shape& shape 是一个参数,它表示一个对 Shape 类型的常量引用。这个参数可以接受任何派生类的对象,因为它是通过引用传递的,可以避免对象的复制。

(1)const: 表示这个引用是常量引用,即在函数内部不能通过这个引用修改传递进来的对象。

(2)Shape&: 表示这是一个对 Shape 类型的引用。

(3)shape: 是参数名称,在函数体内可以使用这个名称来访问传递进来的对象。

        在函数 drawShape 中,可以传递 Shape 类型的对象,也可以传递任何继承自 Shape 的派生类对象这是因为 C++ 允许使用基类的引用或指针来引用派生类的对象,这就是多态性的体现。在运行时,根据实际对象的类型,会动态地调用相应的虚函数。

Shape* shapePtr = new Circle;
drawShape(*shapePtr); // 通过基类引用调用派生类的函数
delete shapePtr;

        在这个例子中,shapePtr 是一个指向 Circle 对象的指针,通过 drawShape 函数传递进去后,依然可以正确调用 Circle 类中的 draw 函数,这就是多态性的体现。

2.5 多态性体现在哪里

2.5.1 动态绑定(Dynamic Binding)

         多态性允许在运行时动态绑定函数调用。当通过基类指针或引用调用虚函数时,实际调用的是对象的实际类型对应的函数。这使得程序更加灵活,能够根据对象的实际类型选择正确的函数实现。

Shape* shapePtr = new Circle;
shapePtr->draw(); // 调用的是 Circle 类中的 draw() 函数
delete shapePtr;

2.5.2 基类引用或指针可以引用派生类对象

        当使用基类类型的引用或指针时,它可以指向或引用派生类的对象。这允许在不了解对象的实际类型的情况下,通过基类接口操作派生类对象。

const Shape& shape = Circle(); // 使用派生类对象初始化基类引用
shape.draw(); // 调用的是 Circle 类的 draw() 函数

2.5.3 虚函数和纯虚函数

class Shape {
public:
    virtual void draw() const; // 虚函数
    virtual void foo() const = 0; // 纯虚函数,要求派生类提供实现
};

2.5.4 可替换性和可扩展性

        多态性使得代码更加可替换和可扩展。通过派生类重写虚函数,可以保留基类的行为并添加新的实现,从而实现了可扩展性。基类引用或指针可以用于处理派生类对象,实现了可替换性。

        总的来说,多态性通过动态绑定、基类引用或指针引用派生类对象、虚函数和纯虚函数等特性,使得面向对象程序更加灵活、可扩展、可维护。

2.6 const Shape& shape = Circle()//使用派生类对象初始化基类引用

        在这个语句中,Circle() 是创建了一个 Circle 类型的匿名对象。这个匿名对象是在栈上创建的,它的生命周期仅在这个语句中。通过 const Shape& shape = Circle();,这个匿名对象被绑定到一个对 Shape 类型的常量引用 shape 上。

        具体而言,这是一个利用派生类对象初始化基类引用的例子。假设 Circle 类是 Shape 类的派生类,这样的初始化是合法的,因为派生类的对象可以被视为基类的对象。

        这样的写法在多态性中很常见,因为它允许我们使用基类引用或指针操作派生类对象。在运行时,实际类型为 Circle 的对象的虚函数调用将调用 Circle 类中的对应函数。

#include <iostream>

class Shape {
public:
    virtual void draw() const {
        std::cout << "Drawing a shape.\n";
    }
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a circle.\n";
    }
};

int main() {
    const Shape& shape = Circle(); // 派生类对象初始化基类引用
    shape.draw(); // 调用的是 Circle 类的 draw() 函数
    return 0;
}

        在这个例子中,shape 引用被初始化为一个 Circle 类型的对象,但通过 shape 调用 draw() 时,调用的是 Circle 类中的 draw() 函数。这是因为 draw() 是虚函数,并且 shape 引用的实际类型是 Circle。

进一步解释:

        Circle() 是一个创建 Circle 类型对象的表达式,这种语法结构称为对象的直接初始化。在这里,Circle() 被用作构造函数调用,它会创建一个 Circle 类型的匿名对象。

        在 C++ 中,类的实例化通常通过调用类的构造函数来完成。如果没有提供构造函数的参数,可以使用空的圆括号 () 来调用默认构造函数,即不带参数的构造函数。

        具体来说,Circle() 表达式会调用 Circle 类的默认构造函数,这是一个不带参数的构造函数。这个构造函数会初始化 Circle 对象的各个成员变量,并执行构造函数中的其他操作。在这种情况下,Circle() 创建了一个默认构造的 Circle 对象。

        以下是一个简单的例子,演示了默认构造函数的使用:

#include <iostream>

class Circle {
public:
    // 默认构造函数
    Circle() {
        std::cout << "Circle object created.\n";
    }
};

int main() {
    Circle c1; // 使用默认构造函数创建 Circle 对象
    Circle c2 = Circle(); // 使用默认构造函数创建 Circle 对象,等效于 Circle c2;
    return 0;
}

        在这个例子中,Circle() 被用作构造函数调用,创建了两个 Circle 对象。注意,C++ 中也可以省略 (),直接写成 Circle c2;,效果是相同的。

  • 51
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值