Interview_C++_day14

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\)++ 中,这三种情况下拷贝构造函数会被使用:

  1. 一个对象以值传递的形式传入函数内。
  2. 一个对象以值传递的形式从函数返回。
  3. 一个对象通过另一个对象初始化。

优点:可以很容易的复制对象。

缺点:对象的隐式拷贝是 \(C\)++ 中是错误和性能问题的来源之一。它也降低了代码的可读性,并使得对象子程序中的传递和改变变得难以跟踪。

赋值函数

Node a;
Node b;
b = a;

这里的 \(b\) 已经存在的,在通过 \(a\) 赋值给 \(b\)

赋值函数重载形式:

Node& operator=(const Node &other) {

}

拷贝构造函数和赋值函数区别

  1. 拷贝构造函数是在对象初始化时,分配一块空间并初始化,而赋值函数是对已经分配空间的对象进行赋值操作。
  2. 实现上,拷贝构造函数是构造函数,通过参数的对象初始化产生一个对象。赋值函数则是把一个对象赋值给另一个对象,需要先判断两个对象是否是同一个对象,若是则什么都不做,直接返回,若不是则需要先释放原对象内存,在赋值。(可以参考 \(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\) 通常按块读取数据会更加快速。

结构体字节对齐原则:

  1. #pragma pack
    1. 结构内部各成员首地址必然是 \(自身大小\) 的整数倍。
    2. \(sizeof\) 最终结果必然是 \(结构内部最大成员\) 的整数倍,不够补齐
  2. #pragma pack(n)
    1. 结构内部各成员首地址必然是 \(min(n, 自身大小)\) 的整数倍。
    2. \(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
posted @ 2020-02-21 19:47  Jiaaaaaaaqi  阅读(103)  评论(0编辑  收藏  举报