C++基础——类(2)

访问控制与封装:

封装是类的成员不能被随意访问的能力,通过把类的设计细节设置为private 就能完成类的封装,封装实现了类的接口和实现的分离。

封装的益处:确保用户代码不会无意间破坏封装对象的状态;

                      被封装的类的具体实现细节可以随时改变,而无需调整用户级别的代码。

访问说明符:public 、 private

定义在public后面的成员在整个程序内可被访问,public成员定义类的接口;

定义在private后面的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private封装了(即隐藏了)类的实现细节。

作为接口的一部分,构造函数和部分成员函数紧跟在public之后;而数据成员和作为实现部分的函数则跟在private说明符之后。

class 和 struct关键字:如果使用struct关键字,则定义在第一个说明符之前的成员是public,class是private。

使用class和struct的唯一区别就是默认的访问权限。

友元:类可以允许其他函数或者类访问它的非公有成员,方法是令其他类或者函数成为它的友元(friend)。友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员,也不受他所在区域访问控制级别的约束。一般来说最好在类定义开始的位置集中声明友元。

友元关系不存在传递性,每个类负责控制自己的友元类或者友元函数。

友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明。

友元声明和作用域:类和非成员函数的声明不是必须在他们的友元声明之前。当一个名字第一次出现在一个友元声明中时,我们隐式地假定该名字在当前作用域中是可见的。然而,友元本身不一定真的声明在当前作用域中。

甚至就算再类的内部定义该函数,我们也必须在类的外部提供相应的声明从而使得函数可见。(友元函数可以定义在类的内部)

类的其他特性:

类型别名:除了定义数据和成员函数之外,类还可以自定义某种类型在类中的别名。由类定义的类型名字和其他成员一样存在访问限制,可以是public或者private中的一种。

内联函数:在类中,常有一些规模较小适合于被声明成内联函数,定义在类内部的成员函数是自动inline的。我们在类的内部可以把inline作为声明的一部分显式地声明成员函数,同样的也能在类的外部用inline来关键字修饰函数的定义。

重载成员函数:和非成员函数一样,成员函数也可以被重载,只要函数之间在参数的数量或者类型上有所区别就行。

可变数据成员:有时我们希望能修改类的某个数据成员,即使是在一个const成员函数内。可以通过在变量的声明中加入关键字mutable做到这一点,一个可变数据成员(mutable data member)永远不会是const,即使它是const对象的成员。因此,一个const成员函数可以改变一个可变成员的值。

返回*this的成员函数:一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。当一个成员调用另一个成员时,this指针在其中隐式的传递。

类类型:即使两个类的成员列表完全一致,他们也是不同的类型。对于一个类来说,它的成员和其他任何类(或者任何其他作用域)的成员都不是一回事儿。我们可以把类名作为类型的名字使用,从而直接指定该类型。或者,我们也可以把类型名跟在关键字class或struct后面。类的声明就像函数的声明一样,我们也能仅仅声明类而不定义它。这种声明有时被称作前向声明,在它声明之后定义之前是一个不完全类型,不完全类型只能在非常有限的情景下使用:可以定义指定这种类型的指针或引用,也可以声明(但是不能定义)以不完全类作为参数或者返回类型的函数。因为只有当类全部完成后类才算被定义,所以一个类的成员类型不能是该类自己。然而,一旦一个类的名字出现后,它就被认为是声明过了(但尚未定义),因此允许类包含指向它自身类型的指针或引用。

类的作用域:

一个类就是一个作用域很好的解释了为什么当我们在类的外部定义成员函数时必须同时提供类名和函数名。在类的外部,成员的名字被隐藏起来了,一旦遇到了类名,定义的剩余部分就在类的作用域之内了,另一方面,因为返回类型的名字在类名之前,所以当成员函数定义在外面时,返回类型中使用的名字都位于类的作用域之外,当然返回类型之前也可以加上类名,使返回类型也在某种类的作用域内。

成员定义中的普通块作用域的名字查找:首先,在成员函数内查找该名字的声明。和前面一样,只有在函数使用之前出现的声明才被考虑。如果,在成员函数内没有找到,则在类内继续查找,这是类的左右成员都可以被考虑。                  如果,类内也没有找到该名字的声明,在成员函数定义之前的作用域内继续查找。

构造函数再探:

有时我们可以忽略数据成员初始化和赋值之间的差异,但是如果成员是const 或者是引用的话,必须将其初始化。类似的,当成员属于某种类类型且该类型没有定义默认构造函数时,也必须将其初始化。以上情况,我们必须通过构造函数初始值列表为这些成员提供初始值。

再很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化在赋值。建议养成使用构造函数初始值的习惯。成员的初始化顺序与他们在类定义中出现的顺序一致,因此构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。

委托构造函数:一个委托构造函数使用它所属类的其他构造函数执行他自己的初始化过程,或者说它把它自己的一些(或者全部)职责委托给了其他构造函数。和其他构造函数一样,一个委托构造函数也有一个成员初始值的列表和一个函数体。在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。和其他成员初始值一样,类名后面紧跟圆括号括起来的参数列表,参数列表必须与类中另外一个构造函数匹配。

在实际中,如果定义了其他构造函数,那么最好也提供一个默认构造函数。

隐式地类类型转换:编译器只会自动的执行一步类型转换。

抑制构造函数定义的隐式类型转换:将构造函数声明为 explicit 加以阻止。关键字explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用于执行隐式转换,所以无需将这些构造函数指定为explicit。只能在类内声明构造函数时使用explicit关键字。

explicit构造函数只能用于直接初始化。

尽管编译器不会将explicit的构造函数用于隐式类型转换,但是我们可以使用这样的构造函数显示地强制进行转换。

聚合类:聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。

类的静态成员:

类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据,类似的静态成员函数也不与任何对象绑定在一起,他们不包含this指针。作为结果,静态成员函数不能声明成const的,而且我们也不能在static函数体内使用this指针。虽然静态成员不属于类的某个对象,但是我们仍然可以使用类的对象、引用或者指针来访问静态成员。但是成员函数不能通过作用运算符就能直接使用静态成员。该关键字只能出现在类内部的声明语句中。

静态成员的类内初始化:即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类的外部定义一下该成员。constexpr用于指定某个表达式或者函数在编译时必须是常量表达式。静态成员必须在类外初始化,静态常量成员可以在类内初始化,即用constexpr修饰的。

静态数据成员可以是不完全类型,可以使用静态成员作为默认实参

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lucky登

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值