c++的目标之一是让使用类对象就像使用标准类型一样。
为什么使用构造函数?
数据的初始化是非常重要的,未经初始化的数据很多情况下都不是期望的结果,很多忘记初始化的数据是导致错误的根源。构造函数就是提供了一个初始化的地方,可以把初始化数据的代码写在构造函数里。当然允许你可以不写在构造函数里,可以自定义一个方法再去调用,不过构造函数的方便之处在于只要创建了对象,就会自动地调用构造函数,保证不会忘记调用。这才是使用构造函数的根本目的。
构造函数的原型和函数头有一个有趣的特征—虽然没有返回值,但没有被声明为void类型。实际上,构造函数没有声明类型。
class Stock { private: std::string company; long shares; double share_val; double total_val; void set_tot() { total_val = shares * share_val; } public: void acquire(const std::string &co, long n, double pr); void buy(long num. double price); void sell(long num, double price); void update(double price); void show() };
由于需要为Stock对象提供3个值,因此应为构造函数提供3个参数。其中一种原型可以是
Stock::Stock(const string& co,long n=0,double pr=0.0)
注意:
Stock::Stock(const string& company,long shares,double share_val)
这种表示是错误的。构造函数的参数表示的不是类成员,而是赋给类成员的值。因此,参数名不能与类成员相同。
为避免这种混乱,一种常见的做法是在数据成员名中使用m_前缀,另一种常见的做法是在成员名中使用后缀_。如
std::string m_company; long m_shares; std::string company_; long shares_;
使用构造函数
C++提供了两种使用构造函数来初始化对象的方式。
第一种是显式地调用构造函数:
Stock garment = Stock("Furry Mason", 50, 2.5);
第二种是隐式地调用构造函数:
Stock garment("Furry Mason", 50, 2.5);
构造函数与new一起使用的方法:
Stock *pstock = new Stock("Furry Mason", 50, 2.5);
这条语句创建一个Stock对象,将其初始化为参数提供的值,并将该对象的地址赋给pstock指针。在这种情况下,对象没有名称,但可以使用指针来管理该对象。
一般来说,使用对象来调用方法,但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象,而不能通过对象来调用。
默认构造函数
默认构造函数是在未提供显示初值时,用来创建对象的构造函数。例如
Stock garment;
如果没有提供任何构造函数,则C++将自动提供默认构造函数。它是默认构造函数的隐式版本,不做任何工作。对于Stock类来说,默认构造函数可能如下:
Stock::Stock() { }
因此将创建garment对象,但不初始化其成员,类似于 int x;
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,程序员就必须为它提供默认构造函数。如果提供了非默认构造函数,如Stock::Stock(const string& co,long n=0,double pr=0.0),但没有提供默认构造函数,则下面的声明将出错:
Stock stock1; //not possible with current constructor
这样做的原因可能是想禁止创建未初始化的对象。然而,要创建对象,而不显式地初始化,则必须定义一个不接受任何参数的默认构造函数。定义默认构造函数的方式有两种。一种是给已有构造函数的所有参数提供默认值:
Stock::Stock(const string& co = "Error", int n = 0, double pr = 0.0);
另一种方式是通过函数重载来定义另一个构造函数—一个没有参数的构造函数:
Stock::Stock();
实际上,通常应初始化所有的对象,以确保所有成员一开始就有已知的合理值。因此用户定义的默认构造函数通常给所有成员提供隐式初始值。如下:
Stock::Stock() { company = "no name"; shares = 0; share_val = 0.0; total_val = 0.0 }
在设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数。
析构函数
用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用析构函数。析构函数完成清理工作,因此实际上很有用。例如,如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。Stock的构造函数没有使用new,因此析构函数实际上没有需要完成的任务。在这种情况下,只需让编译器生成一个什么都不做的隐式析构函数即可。
析构函数和构造函数一样,没有返回值和声明类型。与构造函数不同的是,析构函数没有参数。
Stock::~Stock() { }
由于在类对象过期时析构函数将自动被调用,因此必须有一个析构函数。如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象呗删除的代码后,提供默认析构函数的定义。
对于使用下面这种语法创建并初始化一个名为garment的对象,首先编译器会调用构造函数来创建一个临时对象,然后将该临时对象复制到garment中,并丢弃它。此时编译器将为临时对象调用析构函数。
Stock garment = Stock("Furry Mason", 50, 2.5);