C++指北(3)——类与对象

1.初始化列表

之前我们在讲构造函数时,对成员变量的赋值都是在函数体内进行,然而在面对const成员变量和引用成员变量,没有默认构造的类类型时便会无法处理,编译器此时就会报错。

因为这些特殊的成员变量必须是在创建对象时同时完成初始化,而构造函数体内赋值是在对象已经创建好的基础上完成的,这时我们就要用到之前所提到的初始化列表来解决。

初始化列表是以一个冒号:开始,以逗号分隔,每个成员变量后面都有个小括号,放着自己的初始值或者表达式,最后跟着函数体。

每个成员变量在初始化列表中只能出现一次,顾名思义就是,如果能再次初始化一次那还叫初始化吗?

接下来我们将引入一个新的概念,缺省值。

C++11支持在类的成员变量声明部分后面添加一个缺省值,在初始化列表中未显式写该成员变量时,就会作为给初始化列表使用进行初始化。

出现在初始化列表中的会被初始化,那不在的成员变量也会被初始化吗?

无论是否显式写在初始化列表中,每个成员变量都要遍历一次初始化列表,所以尽量使用初始化列表。

像内置类型这种没有资源的,若无缺省值无传参,对于它是否要进行初始化就要看编译器,但基本是随机值。

而自定义类型的成员变量,即便没有缺省值,不在列表中,但有默认构造,编译器也会自动调用它的默认构造来实现初始化,无默认构造时编译器就会报错。

当然静态成员变量除外,因为它是存储在静态区而不是在对象里,所以它的初始化一定要在类外进行。

代码展示如下:

2.类型转换

C++支持内置,自定义类型的隐式转换,但其自定义类型的构造函数需要对应的参数。

例如现在我们创建了一个类为A,在进行创建对象时写下了这串代码。

A aa=1;

这时你第一眼看到这串代码是不是以为这是在构造,其实不然里面还涉及到了拷贝构造(在C++11之前)。

因为C++会先用这个1去构造一个临时对象再用这个临时对象去拷贝构造aa,但编译器遇到这种连续构造加拷贝构造时会优化成直接构造。

因为在确保正确的前提下,会尽可能的减少传参和返回那些可以省略的拷贝。

因此我们在传参时可以直接把值传进去不用再单独的创建一个对象,利用它会构造一个临时对象的特性。

当然,如果是传的常量等,形参前就要加上const了,毕竟权限不能放大。

例如写了一个构造函数,void process(const A& d);

我们传参就可以直接写成process(3);

如果你想要传多参的话,在C++11之后就支持多参传入了。

A aa={1,2};

A aa({1,2});

A({1,2});

3.内部类

如果一个类定义在另一个类里面时,这个内部内就叫做内部类。

它是一个独立的类,跟定义在全局的类相比只是受限于访问限定符,但它有一个好处就是能跟友元类一样可以访问外部类的私有和保护。

当一个类设计出来是专门给另外一个类使用时,就可以把它设计成内部类。

代码展示如下:

4.友元

友元是一种提供了突破类访问限定符的方式,分为友元类和友元函数,在类或者函数的声明前加上一个friend,再把声明放入类中。

外部的友元函数可以访问类中的私有和保护成员,在类中任意位置都可以声明不受访问限定符作用,但只是一个声明。

友元类中的成员函数可以是另外一个类中的友元函数,可以访问该类中所有的私有与保护。

友元类的关系是单向链接的,不具有交换,传递性。

友元虽然有时很方便但是会增加耦合性破坏类的封装,少用。

5.匿名对象

打个比方,我创建了一个名叫data的类,在创建对象时,正常的创建方式是

date d1;这就叫做有名对象,名指的就是如同d1这样的名字。

但不能写成data d1();与函数声明一模一样,编译器无法分辨会直接报错。

而匿名对象则是取消名字直接用类名来创建对象data();

匿名对象的生命周期只有它所在的这一行,能够传参和调用函数使用。

和临时对象的作用挺像的。

一般临时定义一个对象时拿来用就行了。

6.static成员

用static修饰的成员变量叫静态成员变量,因为它不存在于对象中,存在于静态区中,所以对于它的初始化要在类外进行。

在含有静态成员变量声明的类,创建的所有对象都可以使用该静态成员变量。

既然有静态成员变量,那么就有静态成员函数。

与之相同,在函数名前加static就可以了。

它没有this指针,不可以访问非静态成员变量,而普通的成员函数两者都可以正常的访问。

静态成员变量是成员变量,同样受访问限定符的作用,但不可以在声明处给缺省值,因为它存在于静态区内。

静态成员变量即使被限制为私有,也能通过域作用符访问,因为它属于这个类而不是某个对象。

但是处于保护状态是不可以通过域作用符访问。

静态成员变量的析构调用优先级在局部之后,全局之前。

代码展示如下:

class data
{
public:
    static void func()
    {
        cout << _a << endl;
    }
private:
    static int _a;
};

int data::_a = 0;

7.对象拷贝时的编译器优化

在隐式类型转换时,我们就已经提到了编译器在遇到连续的构造加拷贝构造时,会直接优化成构造(尽管C++并没有明确的规定)。

还有在传参与返回对象所涉及的对象拷贝,大多数编译器也进行了优化。

当一个函数返回一个局部对象时,语法来说是要先构造一个临时对象再将这个局部对象拷贝给临时对象,在VS2022中优化的更厉害,变成直接构造。

传值传参也是直接被优化成构造目标参数了。

并且有的编译器会进行跨行优化处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值