C++类和对象之创建销毁对象和初始化列表

目录:


对象的创建 — 构造函数


初始化表达式

例:构造函数初始化


对象的销毁 — 析构函数

例:析构函数的调用




对象的创建


在之前的 Computer 类中,通过自定义的公共成员函数 setBrand 和 setPrice 实现了 对数据成员的初始化。实际上, C++ 为类提供了一种特殊的成员函数 — 构造函数 来完成相同的工作。构造函数有一些独特的地方:

  • 函数的名字与类名相同
  • 没有返回值
  • 没有返回类型,即使是 void 也不能有
  • 对象在创建的时候,调用构造函数

构造函数在对象创建时自动调用,用以完成对象成员变量等的初始化及其他操作(如为指针成员动态申请 内存等);如果程序员没有显式定义它,系统会提供一个默认构造函数(无参)。下面我们用一个点 Point 来举例:

class Point 
{ 
public: 
	//即使不写,编译器也会自动提供一个
	//构造函数
	Point()
	{
		cout << "Point()" << endl;
		_ix = 0;
		_iy = 0;
	}

	void print()
	{
		cout << "(" << _ix
			 << "," << _iy
			 << ")" << endl;
	}

private:
	int _ix;
	int _iy;
};

int main(void)
{
	Point pt;
	pt.print();
	return 0;
}

编译器自动生成的缺省(默认)构造函数是无参的,实际上,构造函数可以接收参数,在对象创建时提供更大的自由度。我们在上面的 Point 类中可以加入一个新的构造函数

class Point 
{
public:    
	//...
	Point(int ix, int iy)
	{   
		cout << "Point(int,int)" << endl;
		ix = ix;
		_iy = iy;
 	 }
 	 //...
};

int main(void)
{
	Point pt(1, 2);
	pt.print();
	
	Point pt2(11, 12);
	pt2.print();
	return 0;
}

上面的例子同时出现了无参构造函数,和有参构造函数,这说明了 构造函数是可以重载的

在上面,我们说编译器会自动给 Point 类生成一个默认构造函数,这是有前提条件的,就是类中没有定义任何构造函数。现在假设 Point 类中只 显式定义了一个有参构造函数,则编译器不会再自动提供默认构造函数,如果还希望通过默认构造函数创建对象,则需要显式定义一个默认构造函数。



初始化表达式

(初始化表达式是对数据成员进行真正的初始化)

在上面的例子中,构造函数对数据成员进行初始化 时,都是在函数体内进行的。除此以外,还可以通过 初始化列表 完成。

  • 初始化列表位于构造函数形参列表之后,函数体之前,用冒号开始
  • 多个数据成员,再用逗号分隔
  • 初始值放在一对小括号中

例子如下:

class Point 
{
public:
	//...
	Point(int ix = 0, int iy = 0)
	: _ix(ix)
	, _iy(iy)
	{
		cout << "Point(int,int)" << endl;
	}
	//... 
};

如果没有在构造函数的初始化列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。如在 “对象的创建” 部分的两个构造函数中的 _ix 和 _iy 都是先执行默认初始化后,再在函数体中执行赋值操作。有些时候成员必须在初始化列表中进行,否则会出现编译报错。

注意:

  • 每个成员在初始化列表之中只能出现一次
  • 初始化的顺序是由成员变量在类中被声明时的顺序决定的 ,不是由成员变量在初始化列表中的顺序决定的
class Foo
{
public:
	Foo(int a) 
	: _iy(a)   //在初始化列表中,_iy好像先被初始化
	, _ix(_iy)
	{
		cout << "Foo(int)" << endl;
	}
	
private:
	int _ix;  //在声明时,_ix在前
	int _iy;
};

例:构造函数初始化

#include <iostream>

using std::cout;
using std::endl;

class Coord
{
public:
    Coord(int val)
	: _iy(val)
	, _ix(_iy)
    {
        cout << "Coord(int)" << endl; 
    }

    void print()
    {
        cout << "ix = " << _ix 
             << ",iy = " << _iy << endl;
    }

private:
    int _iy;
    int _ix;
};

int main(int argc, char **argv)
{
    Test coord(1);
    coord.print();
    return 0;
}



对象的销毁

构造函数在创建对象时被系统自动调用,而析构函数(Destructor)在对象被撤销时被自动调用,相比构造函数,析构函数要简单的多。析构函数有如下特点:

  • 与类同名,之前冠以波浪号,以区别于构造函数。
  • 析构函数没有返回类型,也不能指定参数。因此,析构函数只能有一个,不能被重载
  • 对象超出其作用域被销毁时,析构函数会被自动调用

析构函数在对象撤销时自动调用,用以执行一些清理任务,如释放成员函数中动态申请的内存等。如果程序员没有显式的定义它,系统也会提供一个默认的析构函数。例如:

class Point 
{
public:
	//...
	~Point() {}
	//...
};

由于 Point 类比较简单,数据成员中没有需要进行清理的资源,所以即使不显式定义析构函数,也没关系。我们再举一个例子:

class Computer
{
public:
	Computer(const char * brand, double price)
	: _brand(new char[strlen(brand) + 1]()) 
	, _price(price)
	{}

	~Computer()
	{
		delete [] _brand;
		cout << "~Computer()" << endl;
	}
	
private:
	char * _brand;
	double _price;
};

以上的 Computer 中,有一个数据成员是指针,而该指针在构造函数中初始化时已经申请了堆空间的资源,则当对象被销毁时,必须回收其资源。此时,编译器提供的 默认析构函数是没有做回收操作的,因此就不再满足我们的需求,我们必须 显式定义一个析构函数,在函数体内回收资源

析构函数除了在对象被销毁时自动调用外,还可以显式手动调用(p.~Computer()),但一般不建议这样使用。

析构函数的调用时机:

  1. 对于全局定义的对象,每当程序开始运行,在主函数 main 接受程序控制权之前,就调用构造函数创建全局对象,整个程序结束时,自动调用全局对象的析构函数。

  2. 对于局部定义的对象,每当程序流程到达该对象的定义处就调用构造函数,在程序离开局部对象的作用域时调用对象的析构函数。

  3. 对于关键字 static 定义的静态局部对象,当程序流程第一次到达该对象定义处调用构造函数,在整个程序结束时调用析构函数。

  4. 对于用 new 运算符创建的对象,每当创建该对象时调用构造函数,当用 delete 删除该对象时,调用析构函数。

例:析构函数的调用

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>

using std::cout;
using std::endl;

class Student
{
public:

    Student(const char * name, int score)
    : _name(new char[strlen(name) + 1]())
    , _score(score)
    {
        strcpy(_name, name);
        cout << "Student" << endl;
    }

    void print() const
    {
        cout << "name:" << _name << endl
             << "score:" << _score << endl;
    }

    ~Student()
    {
        delete [] _name;
        cout << "~Student" << endl;
    }

private:
    char * _name;
    int _score;
};


void func()
{
    cout << "func()" << endl;
    Student stu("Smith", 10);
    stu.print();
}

Student stu2("Janes", 20);
void func2()
{
    cout << "func2()" << endl;
    stu2.print();
}


void func3()
{
    cout << "func3()" << endl;
    static Student stu3("Fox", 30);
    stu3.print();
}

void func4()
{
    cout << "func4()" << endl;
    Student * stu4 = new Student("Loess", 40);
    stu4->print();
    delete stu4;
}

int main()
{
    func();
    func2();
    func3();
    func4();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值