C/C++你必须知道的小知识(25)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情


1.3.27 说说类方法和数据的权限有哪几种

参考回答

  1. C++通过 public、protected、private 三个关键字来控制成员变量和成员函数的访问权限,它们分别表示公有的、受保护的、私有的,被称为成员访问限定符。

    关键字 权限
    public 可以被任意实体访问
    protected 只允许子类及本类的成员函数访问
    private 只允许本类的成员函数访问
  2. 下面介绍一个例子。

    父类:

     class Person
     {
     public:
         Person(const string& name, int age) : m_name(name), m_age(age)
         {
         }
    
         void ShowInfo()
         {
             cout << "姓名:" << m_name << endl;
             cout << "年龄:" << m_age << endl;
         }
    
     protected:
         string  m_name;     //姓名
    
     private:
         int     m_age;      //年龄
     };
    复制代码

    子类:

     class Teacher : public Person
     {
     public:
         Teacher(const string& name, int age, const string& title)
             : Person(name, age), m_title(title)
         {
         }
    
         void ShowTeacherInfo()
         {
             ShowInfo();                             //正确,public属性子类可见
             cout << "姓名:" << m_name << endl;        //正确,protected属性子类可见
             cout << "年龄:" << m_age << endl;     //错误,private属性子类不可见
    
             cout << "职称:" << m_title << endl;   //正确,本类中可见自己的所有成员
         }
    
     private:
         string  m_title;        //职称
     };
    复制代码

    调用方:

     void test()
     {
         Person person("张三", 22);
         person.ShowInfo();                  //public属性,对外部可见
         cout << person.m_name << endl;      //protected属性,对外部不可见
         cout << person.m_age << endl;       //private属性,对外部不可见
     }
    复制代码

1.3.28 如何理解抽象类?

参考回答

  1. 抽象类的定义如下:

    纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”,有虚函数的类就叫做抽象类。

  2. 抽象类有如下几个特点:

    1)抽象类只能用作其他类的基类,不能建立抽象类对象。

    2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。

    3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。

1.3.29 什么是多态?除了虚函数,还有什么方式能实现多态?

参考回答

  1. 多态是面向对象的重要特性之一,它是一种行为的封装,就是不同对象对同一行为会有不同的状态。(举例 : 学生和成人都去买票时,学生会打折,成人不会)

  2. 多态是以封装和继承为基础的。在C++中多态分为静态多态(早绑定)和动态多态(晚绑定)两种,其中动态多态是通过虚函数实现,静态多态通过函数重载实现,代码如下:

    class A
    {
    public:    
        void do(int a);    
        void do(int a, int b);
    };
    复制代码

    1.3.30 简述一下虚析构函数,什么作用

参考回答

  1. 虚析构函数,是将基类的析构函数声明为virtual,举例如下:

    class TimeKeeper
    {
    public:    
        TimeKeeper() {}        
        virtual ~TimeKeeper() {}    
    };
    复制代码
  2. 虚析构函数的主要作用是防止内存泄露。

    定义一个基类的指针p,在delete p时,如果基类的析构函数是虚函数,这时只会看p所赋值的对象,如果p赋值的对象是派生类的对象,就会调用派生类的析构函数(毫无疑问,在这之前也会先调用基类的构造函数,在调用派生类的构造函数,然后调用派生类的析构函数,基类的析构函数,所谓先构造的后释放);如果p赋值的对象是基类的对象,就会调用基类的析构函数,这样就不会造成内存泄露。

    如果基类的析构函数不是虚函数,在delete p时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露。

答案解析

  • 我们创建一个TimeKeeper基类和一些及其它的派生类作为不同的计时方法

    class TimeKeeper
    {
    public:
        TimeKeeper() {}
        ~TimeKeeper() {}  //非virtual的
    };
    
    //都继承与TimeKeeper
    class AtomicClock :public TimeKeeper{};
    class WaterClock :public TimeKeeper {};
    class WristWatch :public TimeKeeper {};
    复制代码
  • 如果客户想要在程序中使用时间,不想操作时间如何计算等细节,这时候我们可以设计factory(工厂)函数,让函数返回指针指向一个计时对象。该函数返回一个基类指针,这个基类指针是指向于派生类对象的

    TimeKeeper* getTimeKeeper()
    {
        //返回一个指针,指向一个TimeKeeper派生类的动态分配对象
    }
    复制代码
  • 因为函数返回的对象存在于堆中,因此为了在不使用时我们需要使用释放该对象(delete)

    TimeKeeper* ptk = getTimeKeeper();
    
    delete ptk;
    复制代码
  • 此处基类的析构函数是非virtual的,因此通过一个基类指针删除派生类对象是错误的

  • 解决办法:  将基类的析构函数改为virtual就正确了

    class TimeKeeper
    {
    public:
        TimeKeeper() {}
        virtual ~TimeKeeper() {}
    };
    复制代码
  • 声明为virtual之后,通过基类指针删除派生类对象就会释放整个对象(基类+派生类)

猜你喜欢

转载自juejin.im/post/7110849366344073230