持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情。
1.3.27 说说类方法和数据的权限有哪几种
参考回答
-
C++通过 public、protected、private 三个关键字来控制成员变量和成员函数的访问权限,它们分别表示公有的、受保护的、私有的,被称为成员访问限定符。
关键字 权限 public 可以被任意实体访问 protected 只允许子类及本类的成员函数访问 private 只允许本类的成员函数访问 -
下面介绍一个例子。
父类:
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 如何理解抽象类?
参考回答
-
抽象类的定义如下:
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”,有虚函数的类就叫做抽象类。
-
抽象类有如下几个特点:
扫描二维码关注公众号,回复: 14294693 查看本文章1)抽象类只能用作其他类的基类,不能建立抽象类对象。
2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。
3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。
1.3.29 什么是多态?除了虚函数,还有什么方式能实现多态?
参考回答
-
多态是面向对象的重要特性之一,它是一种行为的封装,就是不同对象对同一行为会有不同的状态。(举例 : 学生和成人都去买票时,学生会打折,成人不会)
-
多态是以封装和继承为基础的。在C++中多态分为静态多态(早绑定)和动态多态(晚绑定)两种,其中动态多态是通过虚函数实现,静态多态通过函数重载实现,代码如下:
class A { public: void do(int a); void do(int a, int b); }; 复制代码
1.3.30 简述一下虚析构函数,什么作用
参考回答
-
虚析构函数,是将基类的析构函数声明为virtual,举例如下:
class TimeKeeper { public: TimeKeeper() {} virtual ~TimeKeeper() {} }; 复制代码
-
虚析构函数的主要作用是防止内存泄露。
定义一个基类的指针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之后,通过基类指针删除派生类对象就会释放整个对象(基类+派生类)