P9-c++对象和类-02构造函数和析构函数总结

1. 构造函数和析构函数总结

假设有这么一个类

#include <string>  
class Stock  // class declaration
{
    
    
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();
};    // note semicolon at the end

C++的目标之一是让使用类对象就像使用标准类型一样,然而,到现在为止,本章提供的代码还
不能让您像初始化int或结构那样来初始化 Stock对象。
也就是说,常规的初始化语法不适用于类型Stock:

int year = 2001; // valid initialization
struct thing {
    
    
	char *pn;
	int m:
};
thing amabob = {
    
    "wodge", -23}; // valid initialization

Stock hot = {
    
     "sukie's Autos, Inc ",  200, 50.25 }; //NO! compile error

不能像上面这样初始化 Stock对象的原因在于,数据部分的访问状态是私有的,这意味着程序不能直接访问数据成员。您已经看到,程序只能通过成员函数来访问数据成员,因此需要设计合适的成员函数,才能成功地将对象初始化(如果使数据成员成为公有,而不是私有,就可以按刚才介绍的方法初始化类对象,但使数据成为公有的违背了类的一个主要初衷:数据隐藏)。
一般来说,最好是在创建对象时对它进行初始化。例如,请看下面的代码:

Stock gift;
gift.buy(10, 24.75);

就 Stock类当前的实现而言,gift对象的 company成员是没有值的。类设计假设用户在调用任何其他成员函数之前调用 acquire(),但无法强加这种假设。
避开这种问题的方法之一是在创建对象时,自动对它进行初始化。
为此,C++提供了一个特殊的成员函数一类构造函数,专门用于构造新对象、将值赋给它们的数据成员。更准确地说,C++为这些成员函数提供了名称和使用语法,而程序员需要提供方
法定义。
名称与类名相同。例如, Stock类一个可能的构造函数是名为 Stock()的成员函数。构造函数的原型和函数头有一个有趣的特征一一虽然没有返回值,但没有被声明为void类型。实际上,构造函数没
有声明类型。

1. 声明和定义构造函数

现在需要创建 Stock的构造函数。
由于需要为 Stock对象提供3个值,因此应为构造函数提供3个参数(第4个值, total_val成员,是根据 shares和 share_val计算得到的,因此不必为构造函数提供这个值。)
程序员可能只想设置 company成员,而将其他值设置为0:这可以使用默认参数来完成(参见第8章)。
因此原型如下所示:

// constructor prototype with some default arguments
Stock(const string &co, long n=0, double pr =0.0);

第一个参数是指向字符串的指针,该字符串用于初始化成员 company. n和p参数为 shares和share_val成员提供值。注意,没有返回类型。原型位于类声明的公有部分。
下面是构造函数的一种可能定义

Stock::Stock(const string & co, long n, double pr)
{
    
    
	company =  co;
	if (n < 0) {
    
    
		std::cerr <<"Number of shares can't be negative;"
	<< company << "shares set to 0. \n";
		shares =0; 
	} else {
    
    
	 	shares = n;
	}
	share_val = pr;
	set_tot();

}

上述代码和本章前面的函数 acquire()相同。
区别在于,程序声明对象时,将自动调用构造函数

2. 使用构造函数

C++提供了两种使用构造函数来初始化对象的方式。
第一种方式是显式地调用构造函数:

Stock food = Stock("World Cabbage", 250, 1.25);

这将food对象的 company成员设置为字符串“ World Cabbage’”,将 shares成员设置为250,依此类推
另一种方式是隐式地调用构造函数:

Stock garment("Furry Mason", 50, 2.5);

这种格式更紧凑,它与下面的显式调用等价

Stock garment = Stock("Furry Mason", 50, 2.5);

每次创建类对象(甚至使用new动态分配内存)时,C++都使用类构造函数。下面是将构造函数与new起使用的方法:

Stock *pstock= new Stock("Electroshock Games", 18, 19.0);

这条语句创建一个 Stock对象,将其初始化为参数提供的值,并将该对象的地址赋给 stock指针。在这种情况下,对象没有名称,但可以使用指针来管理该对象。我们将在第11章进一步讨论对象指针。
构造函数的使用方式不同于其他类方法。一般来说,使用对象来调用方法:

stock1.show(); //stock1 object invokes show() method

但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象,而不能通过对象来调用。

构造函数是一种特殊的类成员函数,在创建类对象时被调用。
构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。另外,构造函数没有声明类型。

通常,构造函数用于初始化类对象的成员,初始化应与构造函数的参数列表匹配。例如,假设Bozo类的构造函数的原型如下:
Bozo(const char * fname,const char * lname); // constructor prototype
则可以使用它来初始化新对象:
Bozo bozetta = bozo("Bozetta","Biggens"); // primary form
Bozo fufu("Fufu","O'Dweeb"); // short form
Bozo *pc = new Bozo("Popo","Le Peu"); // dynamic object
如果编译器支持C++11,则可使用列表初始化:

Bozo bozetta = {
    
    "Bozetta","Biggens"};	//C++11
Bozo fufu("Fufu","O'Dweeb")				// C++11;
Bozo *pc = new Bozo{
    
    "Popo","Le Peu");	//C++11

如果构造函数只有一个参数,则将对象初始化为一个与参数的类型相同的值时,该构造函数将被调用。例如,假设有这样一个构造函数原型:
bozo(int i)
则可以使用下面的任何一种形式来初始化对象:

Bozo dribble =bozo(44) 		// primary form
Bozo roon(66); 				// secondary form
Bozo tubby = 32; 			// special form for one-argument constructors

实际上,第三个示例是新内容,不属于复习内容,但现在正是介绍它的好时机。第11章将介绍一种关闭这项特性的方式,因为它可能带来令人不愉快的意外。
警告:接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值:
Classname object = value;
这种特性可能导致问题,但正如第11章将介绍的,可关闭这项特性。

3. 接受一个参数的构造函数的例子

constructor.cpp

#include <iostream>
 /*
    author:梦悦foundation
    公众号:梦悦foundation
    可以在公众号获得源码和详细的图文笔记
*/

using std::cout;
using std::endl;
class Bozo {
    
    
public:
	int iVar;
	Bozo(int i) {
    
    
		cout << "i = " << i << endl;
		this->iVar = i;
	}

	void show();
};

void Bozo::show()
{
    
    
	cout << "iVar = " << this->iVar <<endl;	
}
int main() 
{
    
    
	cout << "---------------开始--->公众号:梦悦foundation---------------" << endl;
	Bozo dribble =	Bozo(44); 		// primary form
	Bozo roon(66); 				// secondary form
	Bozo tubby = 32; 			// special form for one-argument constructors


	dribble.show();
	roon.show();
	tubby.show();
	cout << "---------------结束--->公众号:梦悦foundation---------------" << endl;

}

运行结果:

meng-yue@ubuntu:~/MengYue/c++/object_class/02$ ./constructor
---------------开始--->公众号:梦悦foundation---------------
i = 44
i = 66
i = 32
iVar = 44
iVar = 66
iVar = 32
---------------结束--->公众号:梦悦foundation---------------
meng-yue@ubuntu:~/MengYue/c++/object_class/02$

可以看到,直接给类实例赋值一个整数也是可以的。
在这里插入图片描述

默认构造函数没有参数,因此如果创建对象时没有进行显式地初始化,则将调用默认构造函数。
如果程序中没有提供任何构造函数,则编译器会为程序定义一个默认构造函数;否则,必须自己提供默认构造函数。

对于未被初始化的对象,程序将使用默认构造函数来创建:

Bozo bubi;// use default
Bozo *pb = new Bozo;  // use default

就像对象被创建时程序将调用构造函数一样,当对象被删除时,程序将调用析构函数。
每个类都只能有一个析构函数。析构函数没有返回类型(连void都没有),也没有参数,其名称为类名称前加上~。例如,Bozo类的析构函数的原型如下:

 // class destructor
~Bozo();

如果构造函数使用了new,则必须提供使用delete的析构函数。

猜你喜欢

转载自blog.csdn.net/sgy1993/article/details/113530667
今日推荐