网站首页 > 技术文章 正文
简述一下虚析构函数,什么作用
- 虚析构函数,是将基类的析构函数声明为virtual
- 虚析构函数的主要作用是防止内存泄露。
定义一个基类的指针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 {};
- 该函数返回一个基类指针,这个基类指针是指向于派生类对象的。
TimeKeeper* getTimeKeeper()
{
//返回一个指针,指向一个TimeKeeper派生类的动态分配对象
}
- 因为函数返回的对象存在于堆中,因此为了在不使用时我们需要使用释放该对象(delete)
TimeKeeper* ptk = getTimeKeeper();
delete ptk;
- 此处基类的析构函数是非virtual的,因此通过一个基类指针删除派生类对象是错误的
- 解决办法: 将基类的析构函数改为virtual
- 声明为virtual之后,通过基类指针删除派生类对象就会释放整个对象(基类+派生类)
什么是虚基类,可否被实例化?
- 在被继承的类前面加上virtual关键字,这时被继承的类称为虚基类
class A
class B1:public virtual A;
class B2:public virtual A;
class D:public B1,public B2;
- 虚继承的类可以被实例化
class Animal {/* ... */ };
class Tiger : virtual public Animal { /* ... */ };
class Lion : virtual public Animal { /* ... */ }
int main( )
{
Liger lg;
/*既然我们已经在Tiger和Lion类的定义中声明了"virtual"关键字,于是下面的代码编译OK */
int weight = lg.getWeight();
}
简述一下拷贝赋值和移动赋值?
- 拷贝赋值是通过拷贝构造函数来赋值,在创建对象时,使用同一类中之前创建的对象来初始化新创建的对象。
- 移动赋值是通过移动构造函数来赋值,二者的主要区别在于
- 拷贝构造函数的形参是一个左值引用,而移动构造函数的形参是一个右值引用。
- 拷贝构造函数完成的是整个对象或变量的拷贝,而移动构造函数是生成一个指针指向源对象或变量的地址,接管源对象的内存,相对于大量数据的拷贝节省时间和内存空间。
仿函数了解吗?有什么作用
- 仿函数(functor)又称为函数对象(function object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符。
class Func{
public:
void operator() (const string& str) const {
cout<<str<<endl;
}
};
Func myFunc;
myFunc("helloworld!");
>>>helloworld!
bool LengthIsLessThanFive(const string& str) {
return str.length()<5;
}
int res=count_if(vec.begin(), vec.end(), LengthIsLessThanFive);
bool LenthIsLessThan(const string& str, int len) {
return str.length()<len;
}
class ShorterThan {
public:
explicit ShorterThan(int maxLength) : length(maxLength) {}
bool operator() (const string& str) const {
return str.length() < length;
}
private:
const int length;
};
C++ 中哪些函数不能被声明为虚函数?
常见的不能声明为虚函数的有:普通函数(非成员函数),静态成员函数,内联成员函数,构造函数,友元函数。
- 为什么C++不支持普通函数为虚函数?
普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时绑定函数。
- 为什么C++不支持构造函数为虚函数?
主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论)
构造函数用来创建一个新的对象,而虚函数的运行是建立在对象的基础上,在构造函数执行时,对象尚未形成,所以不能将构造函数定义为虚函数
- 为什么C++不支持内联成员函数为虚函数?
内联函数就是为了在代码中直接展开,减少函数调用花费的代价。虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(inline函数在编译时被展开,虚函数在运行时才能动态的绑定函数)。
- 为什么C++不支持静态成员函数为虚函数?
静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态绑定的必要性。
静态成员函数属于一个类而非某一对象,没有this指针,它无法进行对象的判别
- 为什么C++不支持友元函数为虚函数?
因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。
C++ 中类模板和模板类的区别
- 类模板是模板定义,不是实实在在的一个类,定义中用到通用类型参数。
- 模板类是是实在在的类定义,是类模板的示例化。类定义中的参数被实际类型所代替。
虚函数表里存放的内容是什么时候写进去的?
- 虚函数表是一个存储虚函数地址的数组,以NULL结尾。虚表(vftable)在编译阶段生成,对象内存空间开辟以后,写入对象中的 vfptr,然后调用构造函数。即:虚表在构造函数之前写入。
- 除了在构造函数之前写入之外,我们还需要考虑到虚表的二次写入机制,通过此机制让每个对象的虚表指针都能准确的指向到自己类的虚表,为实现动多态提供支持。
- 上一篇: C++模板 - 21(静多态与泛型编程)
- 下一篇: 基于Clang库实现C++文件修改
猜你喜欢
- 2025-01-20 C++|类型转换与运行时类型安全检查
- 2025-01-20 C++Qt开发——事件处理函数
- 2025-01-20 百度Linux C++后台开发面试题(个人整理)
- 2025-01-20 怎样才算学会了C++基础,一篇文章学习了解(包含Qt内容)
- 2025-01-20 C++通过aidl与Android系统服务通信(一)
- 2025-01-20 学习阅读C++编译器错误:函数声明中的荒谬错误
- 2025-01-20 朝文分享(54):深入C++(二十一)——多态
- 2025-01-20 c++多态
- 2025-01-20 深入探讨C++多线程性能优化
- 2025-01-20 【C++】C++ 11 新特性:使用示例
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)