i++ 和 ++i 的区别
++i 返回对象的引用,i++ 必须产生一个临时对象保存更改前对象的值并返回,所以导致在大对象的时候产生的比较大的复制开销,效率较低。
// ++i
int& int::operator++() {
*this += 1;
return *this;
}
// i++
const int int::operator++(int) {
int old_value = *this;
++(*this);
return old_value;
}
拷贝构造函数
Node a;
Node b(a);
Node c = a;
这里的 (b) 和 (c) 都是一开始是不存在的,是通过 (a) 对象来构造和初始化的。
拷贝构造函数重载形式:
Node (const Node &other) {
}
如果用户没有自定义拷贝构造函数并且用到了拷贝构造函数,则编译器会生成默认的拷贝构造函数。
在 (C)++ 中,这三种情况下拷贝构造函数会被使用:
- 一个对象以值传递的形式传入函数内。
- 一个对象以值传递的形式从函数返回。
- 一个对象通过另一个对象初始化。
优点:可以很容易的复制对象。
缺点:对象的隐式拷贝是 (C)++ 中是错误和性能问题的来源之一。它也降低了代码的可读性,并使得对象子程序中的传递和改变变得难以跟踪。
赋值函数
Node a;
Node b;
b = a;
这里的 (b) 已经存在的,在通过 (a) 赋值给 (b)。
赋值函数重载形式:
Node& operator=(const Node &other) {
}
拷贝构造函数和赋值函数区别
- 拷贝构造函数是在对象初始化时,分配一块空间并初始化,而赋值函数是对已经分配空间的对象进行赋值操作。
- 实现上,拷贝构造函数是构造函数,通过参数的对象初始化产生一个对象。赋值函数则是把一个对象赋值给另一个对象,需要先判断两个对象是否是同一个对象,若是则什么都不做,直接返回,若不是则需要先释放原对象内存,在赋值。(可以参考 (shared\_ptr)实现)
总结:
- 对象不存在,没有通过别的对象来初始化,就是构造函数。
- 对象不存在,通过别的对象来初始化,就是拷贝构造函数。
- 对象存在,通过别的对象来初始化,就是赋值函数。
虚函数和内联函数
内联函数通常就是将它在调用处 "内敛的" 展开,消除反复调用的额外开销,但是如果函数太长,会使代码臃肿,反而降低效率。
虚函数可以是内联函数,无论是显式还是隐式,(inline) 都只是一个申请,最终由编译器决定是否内联。所以可以用 (inline) 修饰虚函数,但虚函数表现多态性时,不可以内联,只有当知道调用哪个类的函数时,才可以是内联。
空类的大小
在 (C)++ 中规定类的大小不为 (0),空类大小为 (1),当类不包含虚函数和非静态成员时,其对象大小也为 (1)。若存在虚函数,则需要存储一个虚函数指针大小,在 (32) 位上为 (4) 字节。
结构体字节对齐
class A {
};
class B{
public:
A x;
};
sizeof(B) = 1
class B{
public:
inline virtual fun() {
}
};
sizeof(B) = 4
class B{
public:
A x;
inline virtual fun() {
}
};
sizeof(B) = 8
可以发现最后一个的 (sizeof) 并不是单纯的 (1+4=5),而直接变成了 (8),因为存在结构体的字节对齐规则。
结构体字节对齐的根本原因: (1)) 移植性更好,某些平台只能在特定的地址访问特定的数据。(2)) 提高存取数据的速度,(CPU) 通常按块读取数据会更加快速。
结构体字节对齐原则:
- 无 #pragma pack
- 结构内部各成员首地址必然是 (自身大小) 的整数倍。
- (sizeof) 最终结果必然是 (结构内部最大成员) 的整数倍,不够补齐
- 有 #pragma pack(n)
- 结构内部各成员首地址必然是 (min(n, 自身大小)) 的整数倍。
- (sizeof) 最终结果必然是 (min(n, 结构内部最大成员)) 的整数倍,不够补齐。
#include<bits/stdc++.h>
using namespace std;
class A {
char a;
int b;
short c;
};
class B {
int a;
char b;
short c;
};
int main() {
cout << sizeof(A) << endl; // 12
cout << sizeof(B) << endl; // 8
return 0;
}
造成不同结果的原理在于:
- 对于 (class A) 来说,内部字节为
a | b | c |
---|---|---|
1*** | 1111 | 11** |
- 对于 (class B) 来说,内部字节为
a | b | c |
---|---|---|
1111 | 1* | 11 |