C++类与对象(下)--最后的收尾

在这里插入图片描述

内部类

如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在 全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。

#include<iostream>
using namespace std;
class A
{
private:
static int _k;
int _h = 1;
public:
class B // B默认就是A的友元
{
public:
void foo(const A& a)
{
cout << _k << endl; //OK
cout << a._h << endl; //OK
}
};
};
int A::_k = 1;
int main()
{
cout << sizeof(A) << endl;
A::B b;
A aa;
b.foo(aa);
return 0;
}

我们来计算一下对象A的大小:
在这里插入图片描述
static修饰的成员变量,也就是静态成员变量并没有存储在空间里面,所以只算了int _h = 1;的大小,但是B不算,因为内部类不是附属关系,只是说B的类域受到了A的限制

这里还要再说明的是,也会受到访问限定符的限制,如果把class B前面的public换成private也就会报错

内部类默认是外部类的友元类。

看上面的代码,也就是说B可以访问A的私有成员

内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了。

总的来说,C++不喜欢用内部类,用的也不是很多,但是JAVA会用的比较多

再来回顾上次做过的一道OJ题

求1+2+3+…+n

描述

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0<n≤2000<n≤200
进阶: 空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n)

示例1:

输入:
5

返回值:
15

示例2

输入:
1

返回值:
1

用内部类的情况下可以这样做:

class Solution {
// 内部类
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
};
static int _i;
static int _ret;
public:
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return _ret;
}
};
int Solution::_i = 1;
int Solution::_ret = 0;

匿名对象

这是相当重要的知识,后续的学习还是非常常见的

⽤ 类型(实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的 叫有名对象。

来看示例:

//有名对象,生命周期在main函数结束以后
A aa1(1);
A aa2;
//匿名对象
A(1);
A();//无参的时候必须要加括号,否则无法区分,而且匿名对象的生命周期只有一行

匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

匿名对象的应用场景

1)调用构造函数的时候,减少代码的书写量:

Solution s1;
cout << s1.Sum_Solution(10) << endl;
//优化后的代码
cout << Solution().Sum_Solution(10) << endl;

2)匿名对象能否被引用?

答案是可以。匿名对象和临时对象一样也具有常性

正确的书写形式如下:

const A & r=A();

和临时对象不一样的是,匿名对象的生命周期被延长了。

试想一下:

如果用完了,就销毁,那么引用的意义何在?引用不就成了野引用,这里的匿名对象的生命周期取决于引用的生命周期,也就是main函数结束之后再析构

打个比方来说:

匿名对象特别像一次性筷子,但是要去露营了,三天的时间外面没有换筷子的地方,所以也就将就着用了,它的生命周期也取决于编译器

对象拷⻉时的编译器优化

现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返 回值的过程中可以省略的拷⻉。

代码如下:

A f2()
{
A aa;
return aa;
}
int main()
{
    A aa2=f2();
    cout<<endl;
	return 0;
}

逻辑图如下:
在这里插入图片描述
进程地址图如下:
在这里插入图片描述
这里编译器会觉得有些浪费,两次拷贝构造会浪费空间

在VS2019会有第一次优化:
在这里插入图片描述
直接让aa拷贝构造给aa2

如果关闭优化呢?

情况如下:
在这里插入图片描述
代码:

A f2()
{
A aa;
return aa;
}
int main()
{
// 传值传参
A aa1;
f1(aa1);
cout << endl;
// 隐式类型,连续构造+拷⻉构造->优化为直接构造
f1(1);
// ⼀个表达式中,连续构造+拷⻉构造->优化为⼀个构造
f1(A(2));
cout << endl;
// 传值返回
// 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)
// ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022 debug)
f2();
cout << endl;
// 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)
// ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022 debug)
A aa2 = f2();
cout << endl;
// ⼀个表达式中,连续拷⻉构造+赋值重载->⽆法优化
aa1 = f2();
cout << endl;
return 0;
}

22相比较19,传值返回有着很大的不同,22把拷贝构造都拿掉了,22没有创建aa,aa是aa2的别名,aa相当于一个指针,全程没有拷贝。
在这里插入图片描述在这里插入图片描述
22中也可以理解为把临时对象拷贝给了aa1,函数调用的时候

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值