类与对象
默认成员函数
当一个类被创造出来时,虽然我们没有做任何操作,但是里面还是有一些东西的,那么是什么呢?六大天选之子,编译器自动生成的函数,下面我们来学习一下吧。
1. 构造函数
a.概念和特性
- 概念:构造函数是一个对象生成时第一个自动调用且只调用一次的函数,他是用来初始化对象的,这里的初始化就是带上默认值。
- 特性:
- 函数名和类名相同
- 无返回值
- 可以重载
- 自动调用
b.初始化和赋值
大家都知道,每个变量在创建出来时,都是未定义的值。那为什么在java或者其他语言里,变量被创建后就有默认值?这实际上是这些语言在底层就实现了变量的初始化。
这里具体介绍一下C++构造函数是如何初始化对象的。
这里我想问一下,①和②那个是初始化,哪个是赋初值。
答案是:①是初始化,②是赋初值。
我们通常会以为,②是初始化,实则不然,在刚接触类和对象时,构造函数的写法通常是把①省略不写。但实际上,编译器会默认帮我们加上初始化列表(指①),并给成员变量初始化上值,但是这个值是未知的,由编译器决定。所以,对于一个好的程序而言,我们创建的每个变量都应该初始化上值,不要让程序存在未定义的风险。
- 这里加几点初始化列表的注意事项:
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量。eg:
int& tmp;
- const成员变量。eg:
const int tmp;
- 自定义类型成员(且该类没有默认构造函数时)。
eg: B b;
- 成员变量在类中声明次序就是其在初始化列表中的初始化顺序
- 初始化列表只能在构造函数使用。
2. 析构函数
对象既然能被创造出来,当然还有销毁的过程,让我了解一下销毁的过程
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
如何理解对象中的资源,下面我们通过代码来加深理解
需要强调的是,在b对象里申请了资源,一定要自己写析构函数来释放资源,虽然说编译器自己会默认生成析构函数,但是它是不会在里面默认帮你释放申请的资源。
3. 拷贝构造函数
拷贝构造函数,顾名思义,就是用于复制一份一模一样的对象。如果自己不编写,那么编译器自动会生成一份,下面进行代码演示:
a.浅拷贝
上面提到了浅拷贝,那么什么是浅拷贝呢?
浅拷贝:按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
浅拷贝是一种很简单的拷贝,用于内置变量还行,但是用于指针拷贝就会出现问题,如:b1的指针a
和b的指针a
指向的地址相同,也就是说,这b和b1的a共用同一块数据,b中a的改变会影响b1中a的值。这就是浅拷贝带来的问题。
b.深拷贝
浅拷贝会带来问题,那么就由深拷贝来解决。什么是深拷贝呢?
深拷贝:深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
下面通过代码来深入理解:
所以就有了上面析构函数的作用,需要自己手动编写一个来释放内存,否则就会导致内存泄漏。
4. operator= 函数
运算符的重载,和拷贝构造函数一样,用于复制操作,存在的问题也和拷贝构造一样,需要进行深浅拷贝的编写。
代码演示进行深入理解:
需要注意的是返回值,一定要用引用返回,这样才能进行下面的链式赋值。
=运算符的重载的用法和拷贝构造函数基本,这里不多赘述了
5. operator& 函数
运算符&重载,作用就是取对象地址,一般由编译器自动生成,我们也不会过多编写这个函数,编译器自动生成该函数如下:
B* operator&()
{
return this;
}
6.常量 operator& 函数
同operator&函数一样,只不过加了常属性,缩小了范围,编译器自动生成代码如下:
const B* operator&() const
{
return this;
}
上述两个运算符重载函数,如果有不想让别人拿到对象地址可以return 0xffff(错误地址)
回去
补充个知识点:
const成员函数
例如:
class B
{
public:
void test() const
{
a = nullptr;
b = 12;
}
private:
int* a;
double b;
};
解释一下,括号后面的const
是修饰内置this指针
的,这样一来,函数里的任何操作都不能改变成员变量的值。