C++ 经验条款

C++经验谈:

一、绝不让构造函数称为虚函数:

从最简单的思想来看,C++对象模型中是根据虚函数表来管理虚函数的,那么在调用虚函数时,需要找到虚函数表,在对象没有创建成功时是没有虚函数表指针的,构造函数就是构造对象的,在对象没有创建成功之前来寻找虚函数表是不合理的。

一般情况下,编译器会为每个类生成一个公有的默认构造函数,但是有两种特殊情况例外:

一个类显示声明了构造函数,这种情况下编译器不会生成公有默认构造函数,如果程序需要一个默认构造函数,则需要程序员显示地提供。

一个类声明了一个非Public的构造函数,编译器不会生成公有的默认构造函数。

如果类有继承,那么首先完成基类的构造,再完成继承类的构造,在析构时,顺序相反。

如果基类有virtual函数,那么在继承类的构造中还需要有虚函数表的创建。

关于拷贝构造函数的调用:

当类的一个对象去初始化该类的另一个对象时,拷贝构造函数会被调用。

如果函数的形参是类的对象,调用函数进行形参和实参的结合时,拷贝构造函数会被调用。

如果函数的返回值是类对象,函数调用完成返回时,拷贝构造函数会被调用。

避免在构造/析构函数中调用虚函数:

基类的构造函数会在派生类构造函数执行之前被调用,所以当基类构造函数运行时,派生类的数据成员都没有进行初始化。

虚函数的调用机制完全由基类控制,所以如果基类没有完成构造,虚函数的调用机制(既虚函数表)没有完成初始化,通过虚函数实现的所有期望操作都将失败。

Class对象大小与什么有关系:

如果一个class中无任何数据成员,那class的大小时1.

Class中对象的大小仅与对象的数据成员大小有关系,而与对象的函数成员无任何关系。

Class对象非静态数据成员占用内存大小会影响对象大小

Class对象采用的内存对齐策略会影响类对象大小

Class对象中数据成员不会影响对象大小

Class中virtual函数会影响类对象大小,因为virtual-talbe的原因虚函数占4字节空间

关于虚继承影响对象大小:

由于涉及虚函数表和虚基表,会同时增加一个(剁成继承下对应多个)vfptr指针指向虚函数表vftable和一个vbptr指针指向虚基表vbtable,这两者所占的空间大小为:8(或者8乘以多继承时父类的个数),记住有各自的虚函数表指针和虚基类指针,都共享虚基类指针即可。

类对象的大小影响因素不包括静态数据成员,因为累中的静态数据成员分布于全局存储区域,不占用类对象的空间。

运算符重载:

不要重载&&和||运算符,因为重载&&和||,会导致&&和||失去简短求值功能。

如果一个重载操作符是类成员,那么只有当和它一起被使用的左操作数是该类对象时,它才会被调用。如果该操作符的左操作数必须是其他类型,那么重载操作符必须是命名空间成员。

Public private protected三种继承方法:

Public继承时最常用的继承机制,public继承时子类具有最大的权限,公有继承的特点是基类公有成员和保护成员作为派生类的成员时,他们都保持原有的状态,而基类的私有成员仍然是私有的。

Protected是一种比较有特点的继承机制。保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元函数访问,基类的私有成员仍然是私有的。

Private是一种比较少见的继承方法,私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

单继承在构造时,首先是构造基类,然后是进程自身的构造。如果基类仍然存在上层基类,则首先进行上层基类的构造,然后再构造基类,析构的顺序和构造的顺序是严格按照构造顺序的逆序进行的。

不要重新定义继承而来的非虚函数,以防止产生函数覆盖现象。

带默认参数的virtual函数实现时,虚函数采用动态绑定,默认参数采用静态绑定。在基类虚函数中声明的默认参数,即使派生类中重新指定了默认参数,在多态调用时,依然采用基类虚函数中的默认参数

不要试图尝试重载虚函数。

传值调用、引用传值、指针传值三种方式的区别和联系:

1、  值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即使堆栈中开辟内存空间以存放由主调函数传进来的实参值,从而成为实参的一个副本,值传递的特点是被调函数对形式参数的任何操作都作为局部变量进行,不会影响主调函数的实参变量的值,如果想通过传值方式实现两数据的交换,这种方法不可取。

2、  引用传递过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调用函数对形参的任何操作都被处理成指针间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做任何操作都影响主调函数中的实参变量。

3、  指针传递时传值调用的特例,即传的值为主调函数变量的地址。被调函数的形式参数同样在堆栈中为局部变量开辟了内空间,被调函数对局部变量的任何操作都会作用在主调函数变量的地址之上。因此被调函数通过局部形参所做的任何操作都会影响主调函数中的实参变量。

传值调用、引用传值、指针传值三种方式的区别和联系:

1、  值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即使堆栈中开辟内存空间以存放由主调函数传进来的实参值,从而成为实参的一个副本,值传递的特点是被调函数对形式参数的任何操作都作为局部变量进行,不会影响主调函数的实参变量的值,如果想通过传值方式实现两数据的交换,这种方法不可取。

2、  引用传递过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调用函数对形参的任何操作都被处理成指针间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做任何操作都影响主调函数中的实参变量。

3、  指针传递时传值调用的特例,即传的值为主调函数变量的地址。被调函数的形式参数同样在堆栈中为局部变量开辟了内空间,被调函数对局部变量的任何操作都会作用在主调函数变量的地址之上。因此被调函数通过局部形参所做的任何操作都会影响主调函数中的实参变量。

时间: 2024-10-21 07:30:10

C++ 经验条款的相关文章

高效C++ --经验条款(三)

高效C++ --经验条款(三) 给多态基类声明virtual析构函数: "给base classes一个virtual析构函数",这个规则只适用于带有多态性质的base classes身上.这种base classes的设计目的是为了用来"通过base class接口处理derived class对象". 并非所有base classes的设计目的都是为了多态用途.例如标准stirng和STL容器都不被设计作为base classes使用,更别提多态了.某些clas

Effective C++ --经验条款

高效C++ -4 必须返回对象时,别妄想返回其reference: 这句话什么意思呢?就是在一个函数内,如果你需要这个函数返回一个新的对象,那么这个函数的返回值类型就不要定义成引用类型.就直接返回这个类类型. 首先,我们知道在函数传递参数时,传递引用的好处,尤其是对那么比较大的类型,但是对于内建类型和STL的迭代器类型就没有必要使用引用传递,就可以使用值传递.因为对于内建类型传递引用不值得. 下面看一个例子,为何在必须返回对象时,就别想返回对象的引用.因为得到了函数传递引用的好处,有人可能在返回

高效C++ --经验条款(二)

STL迭代器以指针为根据塑造,所以迭代器的作用就像个T*指针,声明迭代器为const就像声明指针为const一样(既声明一个T* const指针),表示这个迭代器不得执行不同的东西,但它所指的东西的值是可以改动的.如果你希望迭代器所指的东西不可被改变(类似const T*指针),需要的是const_iterator. Std::vector<int>  vec; Const std::vector<int>::iteratoriter = vec.begin();   //iter

高效C++ --经验条款(一)

Effective C++ 指针和引用的不同: 指针在定义的时候就必须进行初始化,如果在使用的过程中,这个变量可能指向为空,那么就不要使用引用,就需要使用指针. 指针域引用的另一个重要的不同是指针可以被重新定义赋值以指向另一个不同的对象.但是引用则总是指向在初始化时被指定的对象,以后不能改变,如果改变了应用的值,那么引用指向的对象就被修改了. 在以下情况下你应该使用指针,一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况

《Effective C++》:条款52-条款55

最后这三个条款属于杂项.条款53告诉我们不要忽略警告,虽然程序可以编译通过,但是要搞明白警告信息.条款54和条款55讲解C++的库,一个是TR1文档,一个是Boost.现在C++11标准已定,且有编译器支持.Boost库一些内容已经标准化到C++11,与其学习Boost,不如熟悉C++11中的Boost库内容. 条款53不要轻忽编译器的警告 条款54让自己熟悉包括TR1在内的标准程序库 条款55让自己熟悉Boost 条款53:不要轻忽编译器的警告 许多程序员习惯性的忽略编译器的警告.或许它们认为

Effective Modern C++翻译(5)-条款4

条款4:了解如何观察推导出的类型 那些想要知道编译器推导出的类型的人通常分为两种,第一种是实用主义者,他们的动力通常来自于软件产生的问题(例如他们还在调试解决中),他们利用编译器进行寻找,并相信这个能帮他们找到问题的源头(they're looking for insights into compilation that can help them identify the source of the problem.).另一种是经验主义者,他们探索条款1-3所描述的推导规则,并且从大量的推导情

《Effective C++》:条款32-条款33

条款32确定你的public继承塑模出is-a关系 条款33避免遮掩继承而来的名称 条款32-条款40是介绍继承相关内容的. 条款32介绍public继承塑造出基类和派生类之间的关系. 条款33介绍继承层次中,变量的作用域以及遮掩关系. 条款32:确定你的public继承塑模出is-a关系 以C++面向对象编程,最重要一个规则是:public inheritance(公开继承)意味着"is-a"(是一种)的关系.在这里是"直译",例如class D: public

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息 条款49:学习破解有关STL的编译器诊断信息 用一个特定的大小定义一个vector是完全合法的, vector<int> v(10);    // 建立一个大小为10的vector 而string在很多方面像vector,所以你可能希望可以这么做: string s(10);        // 常识建立一个大小为10的string 这不能编译.string没有带有一个int实参的构造函数.我的一个STL平台像这样告诉我那一点: e

《Effective Modern C++》翻译--条款4:了解如何查看推导出的类型

条款4:了解如何查看推导出的类型 那些想要了解编译器如何推导出的类型的人通常分为两个阵营.第一种阵营是实用主义者.他们的动力通常来自于编写程序过程中(例如他们还在调试解决中),他们利用编译器进行寻找,并相信这个能帮他们找到问题的根源.第二种是经验主义者,他们正在探索条款1-3所描述的推导规则.并且从大量的推导情景中确认他们预测的结果("对于这段代码,我认为推导出的类型将会是-"),但是有时候,他们只是想简单的回答如果这样,会怎么样呢之类的问题?他们可能想知道如果我用一个universa