文章目录
- 面向对象
- 1 对象模型
- 2 对象的生命周期
- 3 多态
- 4 默认参数
- 5 虚析构函数
- 6 纯虚函数
- 7 虚函数
- 9 继承、虚继承
- 10 仿函数
- 11 模板类、成员模板
- 12 抽象类、接口类、聚合类
- 14 final和override关键字
- 15 拷贝初始化和直接初始化
- 16 public、protected、private权限
- 17 对象复用的了解,零拷贝的了解
- 18 知道C++中的组合吗?它与继承相比有什么优缺点吗?
- 19 this 指针
- 20 C 实现 C++ 类
- 21 friend 友元类和友元函数
- 22 成员初始化列表
- 23 运算符重载
- 24 C++中的重载、重写(覆盖)和隐藏的区别
- 25 构造函数
- 26 编译器转化语句
- 27 空类为啥不为0
- 28 类的大小
- 29 data member的布局
- 30 vptr放在类对象中的哪里最好?
- 31 继承中地址转换
- 32 成员函数
- 33 mangling
- 34 多个基类
- 35 函数效能
- 36 成员初始化列表
- 37 继承下的构造扩充
- 38 函数模板
- 39 munch方法
- 例题
面向对象
1 对象模型
C++【对象模型】| 虚函数表 & 多态如何调用虚函数
C++【对象模型】| 【01】简单了解C++对象模型的布局
2 对象的生命周期
2.1 C++编译器的优化
- 【1】用临时对象生成新对象时,临时对象就不产生了,直接构造新对象;
A a1 = A(10); // 与A a1(10);没有区别
- 函数参数传递过程中,对象优先按引用传递,不要按值传递;
- 函数对象返回时,优先返回一个临时对象由于【1】;
- 接收返回值时对象的函数调用时,优先按初始化的方式接收,不要按赋值的方式接收;
// 未优化
Test GetObj(Test t) {
int val = t.,getData();
Test tmp(val);
return tmp;
}
int main() {
Test t1;
Test t2;
t2 = GetObj(t1);
return 0;
}
// 优化
Test GetObj(Test t) {
int val = t.,getData();
return tmp(val);
}
int main() {
Test t1;
Test t2 = GetObj(t1);
return 0;
}
2.2 拷贝赋值和拷贝构造的区别
Test a1;
a1 = Test(10); // 为赋值操作,调用operator=,由于a1是已存在的,会产生临时对象
a1 = 10; // 隐式生成临时对象,10会被转换为Test(10)
2.3 指针和引用指向临时对象
- 不能返回局部的或者临时对象的指针或引用;
Test *p = &A(10); // A在构造好后,在当前语句就会执行析构函数,故p为nullptr
Test &ref = A(10); // 而引用指向的不会,生命周期被延长
3 多态
类的类型
- 若类有虚函数,则*class是运行时期的类型;
- 若类没有虚函数,则*class是编译器的类型;
3.1 动态多态(运行期)
- 【多态】基类指针(引用)指向派生类对象,通过指针(引用)调用同名覆盖虚函数,基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名覆盖方法;
- 子类型多态(
运行期
):虚函数- 强制多态(
编译期/运行期
):基本类型转换、自定义类型转换
底层
- 通过动态绑定,找到虚表指针->虚函数表->对应的函数;
3.2 静态多态(编译期/早绑定)
- 静态多态:函数重载、模板(函数、类);
注意
- 可以将派生类的对象赋值给基类的指针或引用,反之不可;
普通函数
(非类成员函数)不能是虚函数;静态函数
(static)不能是虚函数;构造函数
不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间
中,必须要构造函数调用完成后才会形成虚表指针
);- 内联函数
不能是表现多态性时的虚函数
;
3.3 多态应用情况
3.3.1 当基类的虚函数和子类的同名函数都有默认参数时
子类的默认参数会被覆盖;由于基类的默认参数会在编译期就被压栈;
3.3.2 交换虚表指针
访问类空间的前4个字节,即虚表指针;
3.3.3 基类指针调用子类private属性的虚函数
成员方法是否能调用,是在编译期就决定的,而基类中的虚函数为public,所有编译通过;而基类指针调用子类的
虚函数是运行期才确定;
4 默认参数
形参待默认值的函数
- 给默认值的时候从右向左给;
- 调用效率问题,默认参数直接压栈,否则需要找到变量,在将他压栈;
- 定义出形参默认值,声明也可给形参默认值,但只能出现一次;
5 虚析构函数
虚析构函数是需要通过
基类指针来调用子类
的析构;
5.1 什么时候把基类的析构函数必须实现成虚函数
- 基类的指针(引用)指向new出来的派生累对象时,delete基类指针它调用析构时,必须发生动态绑定,否则导致派生类的析构无法调用;
6 纯虚函数
- 纯虚函数只是一个接口,是个函数的声明而已,它要留到
子类必须实现
;- 特殊的虚函数,在基类中不能对虚函数给出有意义的实现,它的
实现留给该基类的派生类去做
;带纯虚函数的类叫抽象类
,这种类不能直接生成对象
,而只有被继承
,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类
;
7 虚函数
- 类里如果声明了虚函数,这个函数是实现的,它的作用就是为了能让这个
函数在它的子类里面可以被覆盖
(override),编译器就可以使用后期绑定来达到多态了。- 虚函数在子类里面
可以不重写
;- 虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现;
7.1 虚函数、静态绑定、动态绑定
- 一个类定义了虚函数,则编译阶段编译器给该类类型产生一个唯一的虚函数表;主要存储RTTI指针和虚函数地址;当程序运行时,每张表都会加载到内存的.rodata区;
- 一个类内定义了虚函数,则该类定义的对象运行时,内存中起始位置多了个虚表指针,指向相应类型的虚函数表,一个类型定义n个对象都指向同一张虚函数表;
- 一个类内虚函数的个数,不影响对象的内存大小,影响的是虚函数表的大小;
- 若派生类中的方法和基类方法、返回值、函数名、参数列表都相同,则基类的方法是virtual虚函数,则派生类的该方法,自动处理成虚函数;
7.2 成为虚函数
- 虚函数能产生地址,存储在虚表中;
- 对象必须存在;
7.3 不能为虚函数
- 构造函数,不可以;由于构造函数调用任何函数都是静态绑定,不会发生静态绑定派生类对象构造过程;
- 先调用的是基类的构造函数,在调用派生类的构造函数;
- 静态函数;
- 内联函数;
7.4 析构为什么可以为虚函数
- 在析构函数中,对象依旧存在;
7.5 是不是虚函数的调用一定式动态绑定
不是,类构造中调用虚函数,就是静态绑定;
若不是通过指针或引用来调用虚函数,就是静态绑定;
9 继承、虚继承
9.1 继承
- 代码复用;
- 派生类从基类继承所有成员,除构造和析构;
- 通过继承,在基类里给所有派生类可以保留统一的纯虚函数,等待派生类进行重写,通过使用多态,可以通过基类的指针访问
不同派生类对象的同名覆盖方法;
若不想要被继承
将构造函数设置为private
派生类如何初始化从基类继承来的成员变量
- 通过调用基类相应的构造函数来初始化;
- 通过基类析构来负责清理;
派生类对象构造和析构过程
- 派生类调用基类的构造函数,初始化继承成员;
- 调用派生类自己的构造函数,初始化自己的成员;
- 调用派生类的析构函数,释放派生类成员;
- 调用基类的析构,派生类中从基类继承来的成员;
基类和派生类的转换
- 从下到上是安全的;
- 从上到下是不安全;
9.2 虚继承
虚继承用于解决
多继承
条件下的菱形继承问题(浪费存储空间
、存在二义性
);
- 当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
- 出现虚继承时会讲将基类继承的部分放置到类的最尾部,并在头部添加一个vbptr指向一个虚基类表,虚基类表中记录了
虚基类与本类的偏移地址
;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间;
基类指针指向子类时,出现的问题
- 故在内存释放的时候会出现错误
虚继承:
- 虚基类
依旧存在继承类中
,只占用存储空间- 虚基类表存储的是虚基类相对直接继承类的
偏移
9.3 菱形继承
A: m_a
B:public A{m_a, m_b}
C:public C{m_a, mc}
D:public B, public C {m_a, m_b, m_a, m_c, m_d}
- 以上简述了菱形继承;D中继承会存在两份的m_a,浪费空间;
- 故使用虚继承来解决,B,C中的都放置了虚基类表指针,而m_a被移动到D中保存一份,需要注意此时m_a的初始化需要交给D,若A中没有默认构造,D需要手动初始化;
10 仿函数
把operator()进行函数重载的对象;
【好处】:
- 通过函数指针调用函数,是没有办法内联,效率低,有函数的开销;
- 函数对象是用类生成的,可以添加相关的成员变量,用来记录函数对象使用时更多的信息;
【一般使用在】:
- 泛型算法参数传递;
- 比较;
- 优先级队列;
- 智能指针;
11 模板类、成员模板
- 模板类中可以使用
虚函数
;- 一个类(无论是普通类还是类模板)的
成员模板
(本身是模板的成员函数)不能是虚函数
;
12 抽象类、接口类、聚合类
- 抽象类:含有
纯虚
函数的类;
- 让所有子类复用该熟悉;
- 给所有子类保留统一的重写接口;
- 不可被实例化,但可定义指针和引用变量;
- 接口类:仅含有纯虚函数的
抽象类
;- 聚合类:用户可以
直接访问其成员
,并且具有特殊的初始化语法形式。满足如下特点:
- 所有成员都是 public;
- 没有定义任何构造函数;
- 没有类内初始化;
- 没有基类,也没有
virtual
函数;
14 final和override关键字
- override,避免子类在重写基类函数的时候,防止写错被当成新函数,若写错编译不通过;
- final不希望被子类重写;
15 拷贝初始化和直接初始化
- 直接初始化调用与实参匹配的构造函数;
- 拷贝初始化总是调用拷贝构造函数;
- 拷贝初始化使用指定的构造函数创建一个临时对象,在将临时对象拷贝到正在创建的对象;
16 public、protected、private权限
- public:外部可以访问;
- protected:可被子类访问;
- private:外部不能访问;
继承权限:
- 外部只能访问对象public成员;
- 在继承结构中,派生从基类可继承private成员,但无法访问;
- 若派生类和外部不打算访问,则用private继承;
- 若派生类要访问,不被外部访问用protected;
默认继承权限
- 要看是class还是struct;
- class则为private;struct则为public;
17 对象复用的了解,零拷贝的了解
对象复用
- 创建一个对象池,对对象重复利用,避免多次创建销毁;
零拷贝
- 一种避免CPU将数据从一块存储拷贝到另一块中;
- 减少了数据拷贝和共享总线操作的次数;
- 如:【vector的emplace_back()实现零拷贝,直接将元素原地构造插入,而push_back需要使用拷贝构造和移动构造】
18 知道C++中的组合吗?它与继承相比有什么优缺点吗?
继承
- 优点:子类可以重写对非类的扩张;
- 缺点:父类的内部细节对子类的是可见的;
- 无法在运行期改变父类继承方法的行为;
- 子类和父类是高耦合;
组合
- 类作为另一个类的成员变量;
- 被包含的对象对于当前对象是不可见的;
- 当前对象和包含对象是一个低耦合关系;
- 当前对象在运行时可动态绑定包含对象;
19 this 指针
this 指针是一个
隐含
于每一个非静态成员函数中的特殊指针
,它指向调用该成员函数的那个对象
:
- 一个类型有很多个对象,它们的成员变量是私有的,而成员方法是共享的,我们可以通过this指针来区分是那个对象调用该成员方法;
- 当对一个对象调用成员函数时,编译程序
先将对象的地址赋给 this 指针
,然后调用成员函数
将this指针传入该成员函数,每次成员函数存取数据成员
时,都隐式使用 this 指针
;
this 指针被隐含地声明为:
ClassName *const this
,不能给 this 指针赋值;在 ClassName 类的 const 成员函数中,this 指针的类型为:const ClassName* const
,不能对 this 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);
this 并不是一个常规变量,而是个
右值
,所以不能取得 this 的地址
(不能 &this);
在以下场景中,经常需要显式引用 this 指针:
- 为实现对象的链式引用;
- 为避免对同一对象进行赋值操作;
- 在实现一些数据结构时,如 list。
20 C 实现 C++ 类
C 实现 C++ 的面向对象特性(
封装、继承、多态
)
- 封装:使用
函数指针
把属性
与方法
封装到结构体中;- 继承:结构体
嵌套
;- 多态:父类与子类方法的函数指针不同;
21 friend 友元类和友元函数
- 能访问
私有
成员;破坏封装性
;- 友元关系
不可传递
;- 友元关系的
单向性
;- 友元声明的
形式及数量不受限制
;
22 成员初始化列表
- 常量成员,因为常量
只能初始化不能赋值
,所以必须放在初始化列表里面;- 引用类型,
引用
必须在定义的时候初始化,并且不能重新赋值
,所以也要写在初始化列表里面;- 没有默认构造函数的类类型,因为使用初始化列表可以不必调用
默认构造函数
来初始化;- 当调用一个基类的构造函数,拥有一组参数时;
- 当调用一个成员类的构造函数,它拥有一组参数时;
为什么用成员初始化列表会快一些?
- 函数体中的初始化,是所有在数据成员被分配空间后才进行的;
- 列表初始化是数据成员分配空间的时候就进行初始化;
- 对于类型,少了一次调用构造函数的方法,在函数内赋值会调用拷贝构造;
23 运算符重载
- 其优先级和结合律与内置类型一致才可以,不能改变运算符操作数个数;
- 重载方式:成员、非成员运算符,成员运算符少一个参数;
- 下标、箭头运算符必须是成员函数运算符;
=,[],()和->操作符只能通过成员函数进行重载;
<<和>>只能通过全局函数配合友元函数进行重载;
不要重载&&和||操作符,因为无法实现短路规则。
24 C++中的重载、重写(覆盖)和隐藏的区别
重载
编译器做对象运算时,会调用优先调用成员方法的重载运算符,若没有会在全局中查找;
并且若成员方法没有定义该重载
同一作用域下,对同名函数,函数名相同,其参数的类型、顺序、个数不同、static、virtua、const、volatile不同;
不同函数之间水平关系;
调用时根据实参列表对应关系来找到对应的函数;
【C中为什么没有重载】:
- 由于C++有名变换机制,产生函数符号结合参数等,C函数符号只由函数名来决定;
重写
基类和派生类的方法,返回值、函数名一级参数列表都相同,切基类的方法是虚函数,则派生类的方法就是自动处理成虚函数;
- 是父类和子类的垂直关系;
- 要求参数列表都相同;
- 调用方法根据对象的类型;
隐藏
(隐藏子类函数):派生类中的函数屏蔽了基类中的同名函数(和重写的区别在于基类该函数是否为虚函数);
- 两个参数相同,若基类不是虚函数;
- 两个参数不同,无论基类函数是否为虚函数都会被隐藏;
- 基类指针指向派生类时可以调用派生类的覆盖函数;
- 基类指针只能调用基类的被隐藏函数,无法识别派生类的隐藏函数;
25 构造函数
25.1 默认构造函数
编译器合成:
- 类中内含带有默认构造的类成员;
- 带有默认构造的基类;
- 带有虚函数的类:
- 当class声明/继承一个virtual function;
- 当class派生自一个继承串链,其中一个或更多的virtual base classes;
在
编译期
间,必须给vptr设定初值
,且放置地址
,这些都在构造函数中完成;
在编译期不能够确定真正的类;
- 带有一个虚基类的类
- 编译器必须让虚基类在每个子类中的位置,能够在执行期准备好,在编译期不能够确定真正的类;
程序员是否有提供构造函数,若有则在前头插入默认构造;
25.2 移动拷贝构造以及完美转发
函数传递中:
CMyString& &&str 本质上为 CMyString&;
CMyString&& &&str 本质上为 CMyString&&;
而上述参数传递中:&&str会被当作一个左值,并不是一个右值;
故需要通过forward来传递依次来保证不会被改变;
【forward】:识别左值还是很右值,防止右值被当作成左值;
保证高效的string赋值方法
25.3 拷贝构造函数
产生拷贝动作
- 当以一个对象的内容作为另一个对象的初值时;
- 当对象被当作参数传递时;
- 当函数返回一个对象时
一般会导致一个临时类对象的产生;
拷贝构造
可以带参数,但需要给默认参数
;
为什么传引用不会传值
-由于形参的生产需要调用到拷贝构造,会自己调用自己,导致编译错误;
编译器何时自动生成拷贝构造
没有bitwise copy semantics时,编译器将要合成拷贝构造
- 当类内含一个
成员类
,而成员类中声明一个拷贝构造
;- 当类继承一个基类,而该
基类存在一个拷贝构造
时;以上两种情况必须将后者的拷贝构造插入到类中的拷贝构造;
- 当类声明一个或多个
虚函数
;- 当类派生一个
继承串链
,其中一个或多个虚基类时;
拷贝构造的作用
重新设定virtual table指针,当类中出现虚函数即会执行该操作:
- 增加vtbl,内含每一个有作用的虚函数地址;
- 一个指向vtbl的vptr,安插在每一个类对象内;
当使用拷贝构造发生在带虚函数的类上时,该vptr的初始化将会交给它;
25.4 何时需要自定义拷贝构造函数
- ①对于凡是包含
动态分配成员或包含指针成员的类
都应该提供拷贝构造函数;- ②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。
什么情况下会调用拷贝构造函数
- 用一个类初始化另一个类,会产生临时对象,把对象拷贝给临时对象,函数执行完先析构局部变量,在析构临时对象;
- 函数的参数时类对象时;
- 函数返回值时局部对象;
26 编译器转化语句
转化阶段
- 重写每一个定义(占用内存的行为),其中的初始化操作会被剥除;
- class的拷贝构造会被安插;
参数初始化
- 当一个类对象作为参数,会导入临时性的对象,并调用拷贝构造将其初始化,在将临时的对象交给函数,当离开函数时,将会调用其析构函数释放;
- 方法二:以拷贝建构方式,把实际参数建构在位置上,记录在堆栈中,在函数返回之前将其析构;
X xx; // X __temp0
// __temp0.X::X(xx);
foo(xx); // foo(__temp0);
返回值优化
返回值从局部对象中拷贝返回一般做一个双阶段转换:
- 先加上一个额外参数(reference),用来放置拷贝建构的返回值;
- 在return前插入拷贝构造调用的操作,将要返回的内容拷贝到reference中;
X bar() { // void bar(X& __result) 添加额外参数
X xx; // X xx;
// xx.X::X(); 默认构造
// __result.X::XX(xx); 拷贝构造
return xx; // return; 无返回值
}
编译器层面优化
- 编译器将result参数取代named return value(NRV);
- 该优化能大大的提供程序效率,但由于时编译器完成,故不能明确知道是否有被优化,且当函数较为复杂时,也难以被优化;
return class(xxx)
27 空类为啥不为0
由于编译器确保这个
类在内存中能配置到独一无二的地址
,故安插一个char进去;
【win下vs13 15 17 19 20】:C语言不允许定义空类的struct结构体;
【gcc linux/unix】:sizeof(struct{}) = 0;
【C++中】:为1,由于它是一个对象需要内存,需要构造;
28 类的大小
- 语言本身所造成的额外负担,由于
虚基类
的支持,需要在类中增加一个指针大小;- 编译器对特殊情况所提供的
优化处理
,如上述两个编译器的差异;Alignment的限制
,一般会将数值调整到整数倍,以此来提供程序的效率;
29 data member的布局
非静态数据成员在类中的排序顺序和声明一样;排序顺序只符合出现较晚的成员具有较高的地址;
- 数据成员地址并不一定连续,由于可能需要边界的调整;且可能在class内部插入vptr来支持对象模型;
静态数据成员
- 静态数据成员的存取
不会招致空间或时间上的额外负担
;- 其存取方式不需要通过
类对象
;- 若对于一个静态数据成员的地址,会得到一个
指向数据类型的指针
,而不是一个指向类成员的指针;- 如果两个类中都有声明同名的静态数据成员,则会导致命名冲突,而编译器会对其
进行编码让其成为独一无二的名称
;
非静态数据成员
一般通过类的起始位置,在加上该数据成员的偏移位置(在编译时期可知),其效率和struct 成员一样;
如:origin._y ==>&origin + (&Point3d::_y - 1);
该-1操作,是由于指向数据成员的指针,offset值总是被加1;
用于区分一个指向数据成员的指针,用以指出类的第一个成员
和一个指向数据成员的指针,没有指出任何成员
的两种情况;
30 vptr放在类对象中的哪里最好?
【将ptr放在头部】
C++2.0后由于支持虚拟继承以及抽象基类,故开始将vptr放置在头部,但该做法丧失了C兼容性;
31 继承中地址转换
pv = pv3d ? (Vertex*)((char*)pv3d) + sizeof(Point3d) : 0;
32 成员函数
- 静态成员不能为const;
不能用const的原因:一个静态成员函数访问的值是其参数、静态数据成员和全局变量,而这些数据都不是对象状态的一部分。 而对成员函数中使用关键字const是表明:函数不会修改该函数访问的目标对象的数据成员。 既然一个静态成员函数根本不访问非静态数据成员,那么就没必要使用const了
成员函数与非成员函数
float func(const Point3d *_this) {} == float Point3d::func() const {}
静态成员函数
- 不能直接存取class中的
非静态数据成员
;object_count((Point3d*)0) =>0
强转为指针,提供this指针- 不能被声明为
const、volatile、virtual
;- 不需要经由class obj才被调用;
- 可用于回调函数;
33 mangling
- 对于成员函数,为了区分一般会加上当前类的名称,避免派生类中出现重名函数;
- 为了放置函数重载的同名,为此加上了参数的类型和参数个数,来加以区分;
- 如果禁止使用该方法对函数进行别名,那么请使用extern"C";
34 多个基类
vcall thunk用处
- 以适当的offset调整this指针;
- 跳到virtual function;
主次实例
当一个子类内含
n-1个虚表
时,n为上一层基类的个数,此时子类将会有两个虚表产生:
- 主要实例,
与Base1共享
;- 次要实例,
与base2有关
;
将多个virtual tables连锁成一个,形成次要表格
,若要获取可通过主要表格名称加上一个offset
即可获得;
第二个base class 会影响对虚函数的支持有三种情况
【通过一个指向第二个base class的指针,调用
子类虚函数
】
- 当调用时,该指针必须
调整
以指向子类的起始处;【一个指向子类的指针,调用第二个基类中一个
继承而来的虚函数
】
- 子类指针
必须再次调整
,以指向第二个基类子对象;【允许一个
虚函数的返回值有所变化
】:
- 当我们指向第二个基类的指针来调用clone时,该
指针也需要被调整
,否则返回一个指向子类的对象;
35 函数效能
- 建议不要再一个
虚基类
中声明非静态数据成员
;- 经过测试
非成员函数、静态成员函数、非静态成员函数
都被转换为完全相同形式,效率相同;- 虚函数再继承中,
每多一层继承
其执行时间将会明显增加
,由于没增加一层久会多增加一个额外的vptr设定
;
36 成员初始化列表
- 为public才可用;
- 只能指定常量;
初始化可能性高些;
37 继承下的构造扩充
构造内可能扩充:
成员初始化列表
中的数据成员初始化操作将会放入构造中;- 调用
类成员的默认构造
;- 若类有vptr,需要
设定初值
;调用上一层的基类构造
,若基类时多重继承下的第二或后续的基类,则this指针必须要被调整;- 虚基类构造被调用,从左到右,由深到浅;
- 类中的每一个虚基类子对象的
偏移位置要在执行期被存取
;
38 函数模板
39 munch方法
它是如何工作的呢?
- 为每一个需要静态初始化的文件产生一个_sti()函数,含有构造或inline expansions;
__sti()[__sti__matrix_c__identity()] { identity.Matrux::Matrix(); }- 与之对应的产生一个__std()函数,内含析构函数;
- 提供runtime library
munch
函数:一个_main()【作为main的第一个指令】调用__sti(),一个_exit()调用__std();
有了上述方法,那么该如何收集各个对象的sti和std函数呢】
- 使用mn命令,该命令将用于可执行文件,将对象的符号表格项目导到munch中;
- 此时munch会搜索以_sti或__std开头的名称将该函数名称添加到__sti或__std函数的跳离表格中;
- 在将这个表格写到一个小的程序文本文件;
- 在将编译重新激活,将该表格的文件加以编译,整个可执行文件被重新链接;
例题
任意C++编译器,在用临时对象构造新对象时,则临时对象就不产生了,直接构造新对象;