用户自定义一个类,简单的可以看成是一个新的类型,与C++标准里面的数据类型使用差不多。但在面向对象编程中,我们知道任何一个对象必须要通过构造函数才能创建,以及可以将一个对象拷贝给另一个对象,将一个对象作为参数传递给一个函数等。
C++默认函数
- 一个没有任何成员函数的类,其至少有如下四个默认函数
- 默认构造函数
- 默认拷贝构造函数
- 默认“=”运算符重载函数
- 析构函数
既然是默认函数,那也就是说即便类里面一个成员函数没有,这几个函数仍然存在(想来也是必然,就算啥成员函数都没有,但总需要创建对象,赋值,作为参数传递等),先看下几个默认函数长什么样,这里只是为了更加形象的表示,实际类中是看不到这几个函数的,**一旦类里面有与这几个函数同名的函数,那相
应的默认函数都被覆盖了**
class Base{
private:
int a;
int b;
public:
Base(){}; //默认构造函数
Base(const Base & cb) //默认拷贝构造函数
{
//利用对象b对类对象成员属性进行简单的拷贝
this->a = cb.a;
this->b = cb.b;
}
Base & operator=(const Base & cb) //默认“=”运算符重载函数
{
//利用对象b对类对象成员属性进行简单的拷贝
this->a = cb.a;
this->b = cb.b;
}
~Base(){} //析构函数
};
默认构造函数用于创建对象,主要有以下两种方式
class Base{
public:
int a;
int b
};
Base cb;
Base * pcb = new Base();
以上两种创建对象的方式,均会调用默认构造函数调用创建对象。由于这里类B没有任何构造函数,因此直接调用默认构造函数,需要注意,默认构造函数只是为对象申明空间,而并不会将对象成员进行初始化,如下:
class Base{
public:
int a;
int b
};
Base cb;
Base * pcb = new Base();
cout << cb.a << endl;
// cb.a输出为一个随机值
默认拷贝构造函数,主要用在初始化、传值
- 用一个对象初始化另一个对象
- 将一个对象作为函数参数以值传递的方式传递(与引用方式区分)
- 将一个对象作为函数返回值
class Base{
};
void fun(Base ba);
Base fun1();
Base cb;
Base cb1(cb); //调用默认拷贝构造函数
Base cb2 = cb; //调用默认拷贝构造函数
Base cb3;
fun(cb); //调用默认拷贝构造函数
cb3 = fun1(); //调用默认拷贝构造函数
注意Base cb2 = cb; 这里是初始化,并不是赋值!
默认“=”运算符重载函数, 主要用在赋值语句
- 赋值语句与初始化的区别:初始化是在申明对象的时候,同时对其“赋值”,是在分配内存的同时完成内存内容填充;赋值是先分配内存,再从其它对象将内容拷贝到该内存空间。
class Base{
};
void fun(Base ba);
Base cb;
Base cb2; //调用默认构造函数
cb2 = cb; //调用默认“=”操作符重载函数
默认析构函数:在对象销毁(可以是系统自动销毁或者用户主动销毁)时调用。
class Base{
};
int main()
{
Base cb; //当前作用域之后销毁cb对象,此处是main函数结束即销毁对象
Base * pcb = new Base();
delete pcb; //调用delete pcb手动销毁pcb指向的对象
return 0;
}
需要注意默认拷贝构造函数和默认“=”操作符重载函数都会检查对象是否初始化,前面提到过,默认构造函数在创建对象的时候,如果有成员变量,则不会对成员变量进行初始化,因此在利用该对象初始化其它对象或者赋值给其它对象,则会编译出错。
class Base{
public:
int a;
};
int main()
{
Base cb;
Base cb1(cb); //编译错误提示:使用了未初始化的局部变量
Base cb2 = cb; //编译错误提示:使用了未初始化的局部变量
return 0;
}
使用忠告:
- 为自己的每个类重新编写构造函数,至少要有一个没有参数的构造函数(也即替代默认构造函数)。
- 构造函数里面对类成员进行初始化,尽量使用初始化列表的方式
- 如果有用对象进行初始化或者赋值、拷贝、传值,不要依赖类默认的拷贝构造与“=”运算符重载函数。牵涉到深拷贝与浅拷贝,尽量自己实现这两个函数,详细指定如何拷贝。