目录
Chapter 1.关于类的关键字
1. class,struct与union
1.1简述:
class是我们最为熟悉的C++类声明的关键字,便不再多提了,而C++的struct相比C中struct而言很不一样了,已经扩充了很多东西,而union是一种一种特殊的类,相比前两者就比较少用了,但也不排除有派得上用场的时候。
1.2 详解:
struct与class
刚从C转自C++时,大多数人总是仍把struct当作原来熟悉的包含各种数据的结构体,其实不然。士别三日,即更刮目相看。struct已经可以包含成员函数,可以继承,甚至可以多态,class能做到的,它基本上都可以做到。
那为啥还要class呢?那是因为它们二者还是有区别的,而且关键是它们的定位是不同的。
首先,struct与class最本质的区别是默认访问控制----struct是public的,class是private的。struct可以继承class,同样class也可以继承struct,但是public继承还是private继承,取决于子类而不是基类。
简单来说,struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
其次,“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。这一点在Stanley B.Lippman写的Inside the C++ Object Model有过说明。
最后,struct仍包含在C中的简单{}赋初值操作,但是这样简单的copy操作,只能发生在简单的数据结构上,而不应该放在对象上。加入一个构造函数或是一个虚函数会使struct更体现出一种对象的特性,而使此{}操作不再有效。事实上,是因为加入这样的函数,使得类的内部结构发生了变化。而加入一个普通的成员函数呢?你会发现{}依旧可用。其实你可以将普通的函数理解成对数据结构的一种算法,这并不打破它数据结构的特性。
总而言之,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
详细代码及学习自:https://www.cnblogs.com/starfire86/p/5367740.html
union与struct
C++ union结构式一种特殊的类。它能够包含访问权限、成员变量、成员函数(可以包含构造函数和析构函数)。它不能包含虚函数和静态数据变量。它也不能被用作其他类的基类,它本身也不能有从某个基类派生而来。union中得默认访问权限是public。
union类型是共享内存的,以size最大的结构作为自己的大小。每个数据成员在内存中得其实地址是相同的。
即当union的第二个数据成员在赋初值时,会覆盖第一个数据成员的值,存储在同一个地址上。但正因为这个特性,我们可以用它来判断CPU是大端模式还是小端模式,具体实现见下方网址。
union与struct不同就在于,struct是以所有数据成员大小总和作为自己的大小,所有数据成员存储在相邻地址上。
详细代码及学习自:https://blog.csdn.net/xiajun07061225/article/details/7295355
2.private,public与protected
2.1 简述
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
这三个关键字有两种情况,一种是在类内部使用,表示访问权限;另外一种是在类的继承层次中访问时,表示继承方式。
2.2 详解
根据访问权限总结出不同的访问类型,如下所示:
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
友元类 | yes | yes | yes |
一个派生类继承了所有的基类方法,但下列情况除外:
基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。
类的成员函数可以调用类中各个属性的成员。而类的对象只能访问类的public属性成员,而不能访问private和protected属性成员。所以,类的对象访问类中private和protected的时候要借助public成员函数,这就是类的接口函数。这种访问机制实现了类的封装。
派生类类成员访问级别设置的原则:
1、需要被外界访问的成员直接设置为public
2、只能在当前类中或友元类访问的成员设置为private
3、只能在当前类,子类,友元中访问的成员设置为protected,protected成员的访问权限介于public和private之间。
3.friend
3.1 简述
私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦,而友元便是因此产生(ps:原来是为了程序员偷懒)。
友元分为友元类和友元函数。
3.2 详解
3.2.1友元函数
在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。常用于共享数据或运算符重载。
将全局函数声明为友元的写法如下:
friend 返回值类型 函数名(参数表);
将其他类的成员函数声明为友元的写法如下:
friend 返回值类型 其他类的类名::成员函数名(参数表);
但是,不能把其他类的私有成员函数声明为友元。
友元函数实现代码与普通函数相同(可以在类外或类中,不用加friend和类::)
友元函数虽然能使其他类的成员函数直接访问该类的私有变量,提高效率,但同时它也破坏了封装机制,所以尽量不使用成员函数,除非不得已的情况下才使用友元函数。
友元函数的参数:
因为友元函数没有this指针,则参数要有三种情况:
1、 要访问非static成员时,需要对象做参数;--常用(友元函数常含有参数)
2、 要访问static成员或全局变量时,则不需要对象做参数
3、 如果做参数的对象是全局对象,则不需要对象做参数
3.2.2友元类
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:
friend class 类名;
Note:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元。
详细代码及学习自:https://blog.csdn.net/insistgogo/article/details/6608672
4.virtual
5.const
6.inline
6.1 简述
inline 说明符,在用于函数的声明说明符序列时,将函数声明为一个 内联(inline)函数,引入inline的主要原因是用它替代C中复杂易错不易维护的宏函数。
完全在class/struct/union 的定义之内定义的函数,无论它是成员函数还是非成员 friend 函数,均为隐式的内联函数。
6.2详解
编译器在编译阶段完成对inline函数的处理,即对inline函数的调用替换为函数的本体。函数定义时,在返回类型前加上关键字inline即把函数指定为内联,函数申明时可加也可不加。
inline函数相对宏函数有如下优点:
(1)内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
(2)内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
(3)在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。
(4)内联函数在运行时可调试,而宏定义不可以。
使用inline函数,也要三思慎重。inline函数的缺点总结如下:
(1)代码膨胀。
inline函数带来的运行效率是典型的以空间换时间的做法。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
(2)inline函数无法随着函数库升级而升级。
如果f是函数库中的一个inline函数,使用它的用户会将f函数实体编译到他们的程序中。一旦函数库实现者改变f,所有用到f的程序都必须重新编译。如果f是non-inline的,用户程序只需重新连接即可。如果函数库采用的是动态连接,那这一升级的f函数可以不知不觉的被程序使用。
(3)是否内联,程序员不可控。
inline函数只是对编译器的建议,是否对函数内联,决定权在于编译器。编译器认为调用某函数的开销相对该函数本身的开销而言微不足道或者不足以为之承担代码膨胀的后果则没必要内联该函数,若函数出现递归,有些编译器则不支持将其内联。
6.3 inline函数的注意事项
1)使用函数指针调用内联函数将会导致内联失败。
2)如果函数体代码过长或者有多重循环语句,if或witch分支语句或递归时,不宜用内联。
3)类的constructors、destructors和虚函数往往不是inline函数的最佳选择。
4)一个inline函数会在多个源文件中被用到,那么必须把它定义在头文件中。
可以将内联理解为C++中对于函数专有的宏,对于C的函数宏的一种改进。对于常量宏,C++提供const替代;而对于函数宏,C++提供的方案则是inline。C++的内联机制,既具备宏代码的效率,又增加了安全性,还可以自由操作类的数据成员,算是一个比较完美的解决方案。
一般来说,内联机制用于优化规模较小,流程直接,频繁调用的函数。
详细代码及学习自:https://blog.csdn.net/K346K346/article/details/52065524
7.template
8.delete与default
8.1简述
C++11中,将具有合成版本的成员函数(即默认构造函数或拷贝控制成员)定义为 =default 来显式地要求编译器生成合成的版本(即编译器创建的默认xx函数)。
可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝,即在函数的参数列表后面加上=delete来指出我们希望将它定义为删除的。
8.2 详解
二者不同之处有下:
1.=default当用于类内修饰成员的声明,合成的函数将隐式地声明为内联地;当用于类外定义时,其合成的函数是非内联的。=delete只能用于函数第一次声明的时候,这与声明含义在逻辑上吻合,因为=default直到编译器生成代码时才需要,而编译器需要知道一个函数是删除的以便禁止试图使用它的操作。
2.可以对任何函数指定=delete,但只能对编译器可以合成的默认构造函数或拷贝控制成员使用=default。
Note:析构函数不能是删除的成员,否则我们不能销毁该对象及其成员!
关于合成默认构造函数 https://www.cnblogs.com/QG-whz/p/4676481.html
9.final与override
10.explicit
10.1 简述
explicit(显式的)的作用是“禁止单参数构造函数”被用于自动型别转换,其中比较典型的例子就是容器类型。在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数。
10.2 详解
首先, C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
不过,也有一个例外,当除了第一个参数以外的其他参数都有默认值的时候, explicit关键字依然有效, 此时, 当调用构造函数时只传入一个参数, 等效于只有一个参数的类构造函数。
总而言之,explicit用来禁止编译器隐式调用该构造函数,以防止隐式的类型转换而造成不必要的错误,但对显式调用该函数则没有问题。
11.extern与static
12.constexpr
12.1 简述
constexpr函数是指能用于常量表达式的函数。
12.2 详解
constexpr函数必须遵守几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句:
constexpr int first(){return 42}; constexptr int second=first(); //正确,second是一个常量表达式
编译器在编译时验证first函数是常量表达式,将对其调用替换成结果值42.
为了在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。
Note:constexpr函数不一定返回常量表达式,若用一个非常量表达式调用它,则返回非常量表达式。
12.3 inline函数和constexpr函数
和其他函数不同,内联函数和constexpr函数可在程序中多次定义,但内联函数或constexpr函数的多个定义必须完全一致,因此二者通常定义在头文件中。
本文参考,并部分摘抄了上述所推荐的所有博客(毕竟在我看来写的非常好,所以强烈建议前往学习),C++p(C++经典书籍)以及cppreference.com,表示感谢知识传授,本人C++知识还有所欠缺,所以以上难免存在错误,仅供参考学习,如果大家发现错误和需要改进的地方,请大家留言给予宝贵的建议。
建议如果想了解最新C++特性的,前往上面的网址进行查询。