C++进阶
文章平均质量分 82
本专栏已完结,主要参考书目:
1、effective STL
2、effective C++
3、more effective C++
4、深入探索C++对象模型
5、C++新经典:对象模型
Master Cui
这个作者很懒,什么都没留下…
展开
-
C++对象模型9——临时对象的生命周期、模板及实例化分析、内联函数
一、临时对象的生命周期T c=a+b假设T是一个类型,那么上述代码执行时,首先会产生一个临时对象用来存放a+b的结果(拷贝初始化临时对象),然后用该临时对象拷贝初始化c,最后临时对象被释放。如果开启编译器优化选项,那么会直接用a+b的结果初始化c临时性对象被销毁的时机应该是对表达式求值过程中的最后一个步骤。比如下面的代码int main(){ string s1="123"; string s2="qwe"; bool flag; cin>>flag; str原创 2021-03-18 17:11:44 · 959 阅读 · 0 评论 -
C++对象模型8——构造函数和析构函数中对虚函数的调用、全局对象构造和析构、局部static数组的内存分配
一、构造函数和析构函数中对虚函数的调用仍然以https://blog.csdn.net/Master_Cui/article/details/109957302中的代码为例base3构造函数和析构函数的部分汇编代码如下可见直接在构造函数或者析构函数中调用虚函数并不会触发多态机制,函数的地址在编译器就已经确定二、全局对象的构造和析构class test{public: test(){ cout << "test::test()缺省构造函数执行了" &l.原创 2021-03-18 17:07:44 · 244 阅读 · 0 评论 -
C++对象模型7——类的成员函数、反汇编虚析构函数、RTTI、多态的开销
一、类成员函数class test{public: void myfunc(){} virtual void vfunc() {} static void sfunc() {}};void myfunc(){}查看上述代码的可执行文件的符号清单可见,所有的成员函数最终在可执行文件中,都被转化成了具有唯一函数名的成员函数,所以,类的成员函数经过编译器处理后,实质上就是全局函数非static成员函数的转换过程主要就是添加this指针参数并用this指针访问类数据成员,最后改原创 2021-03-18 17:03:42 · 364 阅读 · 0 评论 -
C++对象模型6——g++中虚继承的实现
虚基类的实现法在不同编译器之间差异极大。然而,每一种实现法的共同点在于必须能够在执行期准确描述虚基类在其每一个派生类中的位置。一、虚继承的实现struct A{ int ax; virtual void f1() {} virtual void f2() {}};struct B : virtual public A {原创 2021-03-18 16:59:15 · 388 阅读 · 0 评论 -
C++对象模型5——类对象的内存布局
一、类对象的内存布局1.1、单一继承的类对象布局示例1class base {public: int m_fai; int m_faj;};class derive : public base {public: int m_i; int m_j;};int main(){ base b; derive d; cout<<showbase; cout<<hex<<&b.b_i<<endl; cout&l原创 2021-03-18 16:55:31 · 401 阅读 · 0 评论 -
C++对象模型4——多重继承的对象内存模型、vptr与vtbl的创建与重置的时机、不要在含有虚函数的类的构造函数中调用memset
一、多重继承的对象内存模型class Base1{public: virtual void f() { cout << "base1::f()" << endl; } virtual void g() { cout << "base1::g()" << endl; }};class Base2{public: virtual void h() { cout << "base2::h()" <<原创 2021-03-18 16:50:52 · 646 阅读 · 0 评论 -
C++对象模型3——vptr的位置、手动调用虚函数、从汇编代码看普通调用和多态调用
一、vptr的位置class test{public: int i; virtual void testfunc() {} };int main(){ test a; char* p1 = reinterpret_cast<char*>(&a); char* p2 = reinterpret_cast<char*>(&(a.i)); if (p1 == p2) { //如果a.i和a的地址相同,则成员变量i在a对原创 2021-03-18 16:46:05 · 800 阅读 · 1 评论 -
C++对象模型2——编译器生成构造函数的几种情况
如果程序员没有提供构造、析构、拷贝构造以及operator=,编译器并不会总是提供一个合成版本,比如下面的代码class test{};int main(int argc, char const *argv[]){ test t; return 0;}在Linux下可以使用下面的命令查看可执行文件中的内容nm a.out | c++filt可见,main函数中并没有调用test的构造函数的痕迹。如果一个类中只有基本类型成员和没有生成构造函数的类成员,那么,编译器不认原创 2021-03-18 16:41:36 · 279 阅读 · 0 评论 -
C++对象模型1——类对象的sizeof、static成员、对象模型、this指针
一、类对象的sizeof1、空类对象的sizeofclass test3{ };int main(int argc, char const *argv[]){ test3 t; cout<<sizeof(t)<<endl; return 0;}C++规定,空类对象的sizeof是1,因为如果空类对象的sizeof是0,那么就意味着可以产生不占内存的代码,和计算机的原理违背。所以,编译器在编译代码时,会在test3中安插一个char数据。向test原创 2021-03-18 16:34:11 · 763 阅读 · 0 评论 -
C++编程进阶9(如何将构造函数和非成员函数虚化、无锁单例模式)
三十二、如何动态调用构造函数和非成员函数构造函数一般都不是虚函数,而非成员函数也不能是虚函数,如何才能动态创建对象呢?整体做法就是将创建对象的过程封装在一个基类虚函数中,然后在不同的子类中重写基类的虚函数,用来创建出不同的子类对象1、构造函数的虚化class basetype{public: basetype(){cout<<__func__<<endl;} virtual ~basetype(){cout<<__func__<<endl原创 2021-03-01 09:01:13 · 327 阅读 · 0 评论 -
C++编程进阶8(最好不要实现类型转换运算符、单形参的构造函数与类型转换、临时对象与RVO)
二十九、最好不要实现类型转换运算符示例代码class fraction{public: fraction(int numerator=0, int denominator=1); operator double() const; ~fraction() {cout<<__func__<<endl;}private: int numerator_; int denominator_;};fraction::fraction(int numerator,原创 2021-02-23 08:46:20 · 228 阅读 · 0 评论 -
C++编程进阶7(何时使用成员函数模板,模板类的实参推断与类型转换、继承与数组)
二十六、何时使用成员函数模板关于成员函数模板见https://blog.csdn.net/Master_Cui/article/details/111824152成员函数模板主要用来兼容不同类型的数据,正如链接中的mystack类模板一样,为了使int和double类型的数据能够彼此拷贝和赋值,需要对拷贝构造函数和operator=写成模板的形式如果一个拷贝构造函数或者operator=被声明为成员函数模板,如果设计者没有声明非成员函数模板版本的拷贝构造函数或者operator=,编译器会为你暗原创 2021-02-18 10:26:46 · 199 阅读 · 0 评论 -
C++编程进阶6(public继承与组合、private继承、多重继承、处理模板基类内的名称、如何避免模板代码膨胀)
二十一、public继承与组合public继承是是子类对象is a基类对象的关系,比如QT中的所有组件类都要继承QObject,所以所有的QT组件都是一个QObject。而组合是has a(包含)或者is implemented in terms of(根据一个类实现另一个类),比如https://blog.csdn.net/Master_Cui/article/details/111824108中的mystack的实现就是根据指定的STL容器实现的所以,在实现一个类的时候,组合和public继原创 2021-02-13 14:29:30 · 337 阅读 · 1 评论 -
C++编程进阶5(内联函数、如何降低编译成本、处理继承体系中同名不同参的成员函数、私有虚函数)
十七、内联函数在https://blog.csdn.net/Master_Cui/article/details/106391552中,已经简单的说过内联函数的作用。函数体较小的内联函数经过编译后,可以生成较小的目标码,但是如果内联函数的函数提很大,那么目标码就会很大,运行起来后,占用的内存就会很多,有可能发生换页并降低缓存命中率,导致程序运行效率变慢。所以,内联函数的函数体不能太大(不要超过10行)。只要内联的函数体较小,内联该函数可以令目标代码更加高效。对于存取函数以及其它函数体比较短,性能关键原创 2021-02-08 09:56:39 · 254 阅读 · 0 评论 -
C++编程进阶4(C++中的强制类型转换运算符、不要返回自定义类内部成员的指针,引用和迭代器)
十五、C++中的强制类型转换C语言中的强制类型转换方式有两种T i = (T)exp; T i = T(exp);这两种方式没有差别,C++中也支持这两种方式,但是和C++中的强制类型转换运算符对比,不太容易识别,作用范围不细化。所以,C++中就有了const_cast、dynamic_cast、reinterpret_cast和static_cast四个强制类型转换运算符1、const_cast和static_cast见https://blog.csdn.net/Master_Cui/原创 2021-02-03 11:01:25 · 211 阅读 · 0 评论 -
C++编程进阶3(如何写出正确的operator=、operator运算符的返回值以及是否应该是成员函数的讨论)
八、如何写出一个安全的operator=首先,operator=的返回值通常是一个类的引用,这一点需要和C++标准库的容器类的operator=保持一致其次,如果一个类中含有指针成员,那么要防止自赋值(左值对象和右值对象的指针成员指向同一块内存区域或同一个对象)仍然以https://blog.csdn.net/Master_Cui/article/details/109532520中的mystring为例假如operator=的实现如下mystring & mystring::原创 2021-01-22 20:39:29 · 1404 阅读 · 0 评论 -
C++编程进阶2(编译器在类内默认生成的函数讨论以及纯虚析构函数)
三、编译器默认提供的类内函数讨论1、当写下一个空类时,编译器会其默认提供四个函数:构造、拷贝构造、operator=和析构函数,而且都是public的class Empty{ };上述的Empty相当于下图2、如果在一个类内仅仅自定义了一个构造函数,那么编译器将不会再提供默认构造函数。但是编译器仍然会提供默认的拷贝构造、operator=和析构函数3、当类内函数const对象或者引用对象时,编译器不会提供默认的构造函数和operator=示例class test{原创 2021-01-15 14:36:00 · 248 阅读 · 0 评论 -
C++编程进阶1(对于单纯的常量,用const替换#define、operator[]与const)
一、对于单纯的常量,用const替换#define;用内联函数替换宏函数如果想表示一个常量,请使用const而不是#define,因为#define在预编译期被简单的替换,没有类型检查。而const变量会被编译,编译期间会进行类型检查,更加安全可靠。除了类型检查之外,由于#define只是简单的将宏名替换,所以会导致生成的目标代码更大,而const就不会因为宏只是简单的替换,所以宏函数不会导致函数调用的开销,但是没有参数检查,有可能是不确定的示例#define MAX(a,b) (a)&g原创 2021-01-14 17:58:27 · 598 阅读 · 1 评论 -
C++标准库进阶6
二十二、函数指针和函数调用运算符的效率使用less<int>的sort调用比使用inline comp的sort调用快得多、原因是因为函数内联。less<int>::operator()函数对象的operator()函数是内联的,operator()的函数体可以直接被编译器使用,直接在调用处进行展开。所以,sort中不包含函数调用,编译器就可以对这段不包含函数调用的代码进行优化而函数指针参数抑制了内联机制,因为如果使用函数指针来调用内联函数,那么就需要获取inline函数原创 2021-01-13 08:55:36 · 193 阅读 · 0 评论 -
C++标准库进阶5
十九、使用accumulate或者for_each进行区间统计。accumulate有两种形式template <class InputIterator, class T>T accumulate (InputIterator first, InputIterator last, T init);template <class InputIterator, class T, class BinaryOperation>T accumulate (InputItera原创 2021-01-12 15:22:36 · 210 阅读 · 0 评论 -
C++标准库进阶4
十六、map的operator[]和insertmap对象添加元素的方法有两种,一种是使用operator[],另一种是使用insert方法,那么这两种方法的区别在哪?1、当向map对象中添加一个新的键值对时,请使用insert方法示例class test{public: test() {cout<<__func__<<endl;} test(int val) {cout<<"test(int val)"<<endl;} test(原创 2021-01-12 09:25:47 · 308 阅读 · 0 评论 -
C++标准库进阶3
十三、关联容器的等价与比较函数的返回值两个对象是否相等是基于operator==。因为operator==可以自定义,所以,两个对象相等,并不意味着对象所有的数据成员都相等而关联容器对相同元素的定义是等价,默认是基于operator<。比如有个map<string, int>对象,该对象key的排序方式默认就是operator<,a与b等价关系可以如下表示:!(a<b) && !(b<a)上述表达式的含义是:按照一定的排序规则,a,b中的原创 2021-01-01 19:41:11 · 129 阅读 · 0 评论 -
C++标准库进阶2
接上一篇文章https://blog.csdn.net/Master_Cui/article/details/110739779十、把容器数据传给旧的接口1.vector传入数组型APIvoid stlwitholdapi(int *ps, size_t num){ for (int *p=ps;p<ps+num;++p) { cout<<*p<<endl; }}int main(int argc, char const *argv[]){ v原创 2020-12-29 08:04:09 · 147 阅读 · 0 评论 -
C++标准库进阶1
一、容器的选择一般情况下,最容易想到也最常用的就是vector,但是如果要是频繁在容器对象的中间删除和插入元素,就要考虑使用list,因为list是基于节点的容器,当频繁在容器中间删除和插入元素时,不需要将很多元素整体向前或者向后移动,只需要操作节点元素指针指向的位置,效率较高且不会导致迭代器失效。如果频繁在容器两端删除和插入元素,不仅可以使用vector,也可以使用deque.如果对容器中的元素的顺序有要求的话,可以考虑有序关联内容器,如果对容器对象的查找速度有要求的话,可以使用无序关联容器.原创 2020-12-06 16:16:48 · 179 阅读 · 0 评论