C++中的类和对象——第四篇

一、初始化列表

1.1什么是初始化列表,他是用来干什么的

初始化列表可以理解为每个对象中成员定义的地方,它的用处可以顾名思义,也就是对类中的成员进行初始化

1.2初始化列表的使用举例

当我们希望用栈实现队列的时候,我们需要创建这样一个类

class MyQueue
{
    Stack _pushst;
    Stack _popst;
    int _size;
}

我们已知的是如果我们不显式写构造函数,那么内置类型会给一个随机值,而自定义类型会自动调用它的默认构造函数,那么问题来了:

如果没有默认构造函数呢?也就是在Satck类中我们显式写了构造函数,但是它含有无缺省值的参数,那么我们要怎样让MyQueue类正常进行构造呢?

答:我们可以选择显式实现MyQueue的构造函数,在过程中使用初始化列表:

在构造函数参数列表之后写一个用:(冒号)开始并且用,(逗号)结束,格式为成员名加括号的几条语句,例如

MyQueue(int n=20)
:_pushst(n)
,_popst(n)
,_size(0)
{
    //...
}

此时我们给了Stack类进行构造所需参数,自然可以完成MyQueue的构造了。

1.2补:

初始化列表与函数体内赋值可以混用,但习惯上主要用初始化列表

1.3三种必须要使用初始化列表的情况

1.3.1前两种

const变量和引用变量,如:

private:
    const int _a;
    int& _af;
1.3.1补:

①const变量必须在定义时赋值

②引用变量必须在定义时赋值

1.3.2后一种(对应1.2中的例子)

没有默认构造函数的自定义类型成员,如1.2中的Stack

1.4不写初始化列表的时候会发生的情况

初始化列表,不管你是否显式去写,所有的成员变量都会走一遍,内置类型给随机值,自定义类型自动调用其默认构造函数

1.4补:

C++11以后支持定义成员变量的时候给缺省值,其实本质就是把这个值提供给初始化列表,只是缺省值的优先度比较低,如果在初始化列表中显式写了成员变量的值,那么这时候缺省值会被忽略。

1.5为什么说初始化列表是"自由"的

在初始化列表的括号中,我们可以写一个表达式甚至进行函数调用,

例如:

{
    //...
Stack(int cap)
    :_top(2+3*5)
    ,_capacity((int*)malloc(sizeof(int)*cap))
    ,_size(10)
    ,_a({1,2})//第四个是2.3中介绍的多参数隐式类型转换

pravite:
    int _top;
    int* _capacity;
    int _size;
    A _a;
}

1.5补:

既然缺省值是提供给初始化列表的,而初始化列表“自由”,那么缺省值是否也“自由”呢?

是这样的,在给缺省值的时候,可以为函数或者表达式

如:

{
    //...
    Stack()
        {}

    //...
pravite:
    int _top = 2+3*5;
    int* _capacity = (int*)malloc(sizeof(int)*40);
    int _size = 10;
    A _a = {1,2};
}

1.6初始化列表对成员变量初始化的顺序

该顺序为类中变量的声明顺序,尤其是与在初始化列表中的顺序无关

1.6补:

类对象创建出来以后,成员变量在内存中的存储顺序与在类中声明顺序一致。

二、内置类型直接给类类型赋值的原理(隐式类型转换)

2.1单参数构造函数支持内置类型直接赋值

例如有一个int类型参数的构造函数,我们可以

class A
{
public:
    //构造函数
    A(int a = 0)
    :_a(0)
    {
        cout<<"A(int a)"<<endl;
    }
    //拷贝构造函数
    A(const A& aa)
    {
        cout<<"A(const A& aa)"<<endl;
    }
private:
    int _a;
}

int main()
{
    //这是使用构造函数直接进行构造,打印的结果应为一个构造函数
    A aa1(1);

    //此处包含了int类型的3利用构造函数隐式类型转换为类类型中间变量,再利用拷贝构造函数
    //把中间变量拷贝给aa3
    //但是由于编译器的优化,打印结果依旧只有一个构造
    A aa3=3;

    //此处我们用raa接收了3利用构造函数隐式类型转换出来的类类型中间变量,因为中间变量具
    //有常性,所以我们需要用const修饰一些类型才能顺利接收
    const A& raa=3;
    return 0;
}

代码进行了详细解释,但是这里涉及到了一个编译器的优化:

当编译器遇到了连续(所谓连续,就是像A aa3=3这种一个语句涵盖)的构造+拷贝构造,则直接优化为了一个单独的构造。

2.1补:

当前主流编译器大多都会这样优化,但是像VC6.0等等老式编译器可能不做优化,VS2022等很高级的编译器又会优化的更加彻底(不限于连续,跨行进行优化)

毕竟这一部分C++标准没有明确进行规定。

2.2可不可以拒绝进行隐式类型转换?

可以的,有一个关键字explicit,把它加到构造函数之前,那么这个构造函数将不再支持隐式类型转换,如:

explicit A
{
    //...;
}

2.3单参数构造函数可以,那么多参数呢?

在C++11之后添加了支持,例如对

A(int a1,int a2)
{
    //...
}

我们可以在main函数中

A aa1 = {1,2};

来实现同时满足两个参数,它起到的效果与单参数类似,先构造为临时变量再拷贝构造

三、关于静态static修饰成员变量和函数

3.1static修饰成员变量

3.1.1使用注意事项

①创建对象后,对象中只存非静态成员变量,不存静态成员变量(因为静态成员变量在静态区不在栈区)并且sizeof的结果也不包含静态成员变量 

②静态成员变量不能给缺省值(因为静态成员变量不存在与对象中,所以不走初始化列表)

③必须要并且只能用声明定义的方式给初始值

3.1.2它的价值

静态成员变量属于整个类,属于所有成员,

当它为public属性的时候,还可以这样访问:

//任选对象直接访问
aa1._sta;
//用类类型加::来访问
A::_sta;

3.2static修饰成员函数

使用static修饰后的成员函数将不再有this指针,只能访问静态成员变量,关于它的价值,其一我们只能通过静态成员函数来访问静态成员变量,其二我们可以利用静态成员变量加静态成员函数来完成求1+2+...+n

四、关于类和对象的小知识点

4.1友元函数和友元类

关于友元函数:

作为友元的函数,不可以被const修饰

关于友元类:

(我是你的朋友,但你还不是我的朋友)

class Time
{ 
    friend class Date;
    //...
}

Date可以访问Time中的私有成员,但是Time不能访问Date中的,

这种访问可以像这样:

//(Date中)
Time _t;
_t._hour=hour;

直接创建类类型对象,修改对象中的私有成员变量值

4.1补:

①友元不能传递:A为B的友元,B为C的友元,不能说明A为C的友元

②友元是单向的

③友元不能继承

4.2内部类

假如在类A中写了类B,那么

①他们是独立的类,仅仅是B受到A的类域和访问限定符的限制

A aa1;
A::B b1;

②B天生是A的友元,可以访问A中的私有成员

4.3匿名对象

匿名就是不起名,如

//默认构造创建对象
A aa1;
//传参数2构造对象
A aa2(2);

//匿名对象
A (2);

匿名对象的生命周期只在当前这一行,过了这一行就销毁

4.3补:匿名对象和单参数构造函数创建对象的特殊传参

当写了一个函数f1时,我们要传入其中类类型参数

A aa1;
//正常传入,参数列表可以为A& aa
f1(aa1);
//认为匿名对象具有常性,需要参数列表为const A& aa
f1(A(2));
//单参数构造函数把2隐式类型转换为中间变量,需要参数列表为const A& aa
f1(2);

若要正常进行下去,那么f1需要参数列表为

void f1(const A& aa)
{
    //...
}

4.4优化

对于连续一行的表达式,拷贝构造+拷贝构造会被优化为一个拷贝构造,请看代码

A f2()
{
    A aa;
    return aa;
}
int main()
{
    A ret = f2();
    return 0;
}

分析可知,创建了aa对象,要返回的时候需要拷贝构造为一个中间变量,中间变量再进行第二次拷贝构造赋值给ret,而编译器在遇到这样的代码时,会自动将两个拷贝构造合为一个,节省资源加快速度。

注:把A ret = f2()改为两行

A ret;

ret = f2();

的时候优化将不再进行,所以我们最好不要这么做

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值