- 前言
- 构造函数
- 声明和定义构造函数
- 使用构造函数
- 默认构造函数
- 使用默认构造函数
- 析构函数
- 赋值与初始化
- 对象赋值
- 对象的列表初始化
- 后记
本篇继续类与对象,构造函数和析构函数。
构造函数类的构造函数被用于对象初始化。构造函数也属于类方法。
声明和定义构造函数构造函数的定义与其它类方法相同,即在类声明中声明构造函数,类方法定义中定义构造函数。
构造函数的名字与类名相同,并且没有返回类型,示例如下:
// declare_class.h
#ifndef DECLARE_CLASS_H
#define DECLARE_CLASS_H
class SimpleClass{
private:
int a_;
public:
SimpleClass(int);
void set(int);
int get();
void add(int x) { // inline method
a_ += x;
}
~SimpleClass();
};
#endif
SimpleClass(int)
是类SimpleClass
的构造函数声明,下面提供其定义:
// define_class_method.cpp
#include "declare_class.h"
SimpleClass::SimpleClass(int a) {
a_ = a;
}
注意:定义构造函数时,容易出现的问题是形参名与类成员数据名冲突。形参名不能与类成员名相同。
使用构造函数可以采用显式和隐式的方法来调用构造函数初始化对象,示例如下:
// main.cpp
#include "declare_class.h"
void main(){
SimpleClass sc1(1);
SimpleClass sc2 = SimpleClass(2);
SimpleClass* psc = new SimpleClass(0);
}
隐式调用构造函数初始化sc1
,初始化语句中没有明确指出构造函数名。 显式调用构造函数初始化sc2
,初始化语句直接指出了构造函数名。
也可以使用new
为对象分配动态内存时初始化*psc
。
注意:构造函数是用于创建对象的,已被创建的对象不能调用构造函数。
默认构造函数如果希望创建对象时不显式提供初始值,则需要默认构造函数。
当一个类中没有定义任何构造函数时,编译器将自动提供一个默认构造函数,类似于:
SimpleClass::SimpleClass(){}
此时创建的对象不会进行初始化:
SimpleClass sc; // uninitialized
如果定义了构造函数,则编译器不再提供默认构造函数。例如使用declare_class.h
声明的类时,下面的对象创建语句将报错:
#include "declare_class.h"
void main()
{
SimpleClass sc; // error!
}
由于没有默认构造函数,编译器将报error: no matching function for call to 'SimpleClass::SimpleClass()'
。
因此,定义了其它构造函数时,需要手动提供默认构造函数。
一般而言,要避免创建未初始化的对象。因此,通常有两种提供默认构造函数的方法:①定义所有参数带默认值的构造函数。②重载一个无参的默认构造函数。
第一种方法示例如下:
// declare_class.h
#ifndef DECLARE_CLASS_H
#define DECLARE_CLASS_H
class SimpleClass{
private:
int a_;
public:
SimpleClass(int a=0);
void set(int);
int get();
void add(int x) { // inline method
a_ += x;
}
~SimpleClass();
};
#endif
第二种方法示例如下:
// declare_class.h
#ifndef DECLARE_CLASS_H
#define DECLARE_CLASS_H
class SimpleClass{
private:
int a_;
public:
SimpleClass(int);
SimpleClass();
void set(int);
int get();
void add(int x) { // inline method
a_ += x;
}
~SimpleClass();
};
#endif
// define_class_method.cpp
#include "declare_class.h"
SimpleClass::SimpleClass(){
a_ = 0;
}
使用默认构造函数
同样,可以显式和隐式调用默认构造函数:
SimpleClass sc; // implicitly
SimpleClass sc = SimpleClass(); // explicitly
注意:默认构造函数在隐式调用时不能使用括号,否则就变成了函数声明。
析构函数对象达到生命周期时,在销毁前自动调用析构函数进行空间清理。
析构函数也是类方法,并且和构造函数一样没有返回类型。但析构函数没有参数。并且一个类只能有一个析构函数。
析构函数名是在类名前加一个~
,仍然以SimpleClass
类为例:
// declare_class.h
#ifndef DECLARE_CLASS_H
#define DECLARE_CLASS_H
class SimpleClass{
private:
int a_;
public:
SimpleClass(int a=0);
void set(int);
int get();
void add(int x) { // inline method
a_ += x;
}
~SimpleClass();
};
#endif
~SimpleClass()
是其析构函数。
一般而言,不需要显式提供析构函数,编译器将默认提供一个析构函数。
一般而言,不需要显式调用析构函数。
- 对于静态存储对象,程序结束时会自动调用析构函数;
- 对于自动存储对象,执行完该局部后会自动调用析构函数;
- 对于动态存储对象,当调用
delete
删除其内存空间时会自动调用析构函数。
将一个对象赋值给同类型的另一个变量时,实际上是把一个对象里的每个数据成员复制到另一个对象中:
void main(){
SimpleClass sca(1);
SimpleClass scb(2);
scb = sca; // copy
}
需要区分对象赋值和初始化:赋值是给已有的对象进行数据操作;初始化是创建新的对象并对其进行数据操作。
对象的列表初始化之前的博客已经提过,C++11的一个新特性是列表初始化。这项特性也能用于对象初始化:
void main(){
SimpleClass scc {1}; // constructor
SImpleClass scd {}; // default constructor
}
只要初始化列表中的参数与任何一个该类的构造函数参数列表相符,就能进行列表初始化。
后记C++中类的基础内容到这就基本结束了。下篇会进入类的进阶内容。