9——对象的创建和撤销,构造函数和析构函数

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

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

当对象被创建时,构造函数自动被调用。

构造函数有一些独特的地方:函数的名字与类名相同,没有返回类型和返回值,即使是void也不能有。

1>其主要工作有: 给对象一个标识符。

2>为对象数据成员开辟内存空间。

3>完成对象数据成员的初始化(函数体内的工作,由程序员完成)。

上述3点也说明了构造函数的执行顺序,在执行函数体之前,构造函数已经为对象的数据成员开辟了内存空间,这时,在函数体内对数据成员的初始化便是顺理成章了。 备注代码给出了point类的显式构造函数 。

class point
{
private:
    int xPos;
    int yPos; public:
    point();
};
point::point()
{
    xPos = 0;
    yPos = 0;
} 

构造函数可以有参数:

编译器自动生成的缺省(默认)构造函数是无参的,实际上,构造函数可以接收参数,在对象创建时提供更大的自由度,如下列代码

一旦用户定义了构造函数,系统便不再提供默认构造函数。 跟踪执行,理解构造函数的执行顺序。

//point.h
#include <iostream>
using namespace std;
class point //point类定义,在定义同时实现其成员函数
{
private: //私有成员,分别代表x轴和y轴坐标
        int xPos;
        int yPos;
public:
        point(int x, int y) //有参构造函数
        {
                cout << "对象创建时构造函数被自动调用" << endl;
                xPos = x;         yPos = y;
        }
        void print() //输出信息
        {
                cout << "xPos: " << xPos << ",yPos: " << yPos << endl;
        }
}; 

#include "point.h"
int main()
{
        //    point pt0;//错误的调用,因为我们已经显示的定义了一个带参数的构造函数
        //    pt0.print();//输出pt0的信息
        point pt1(3, 4);    //调用有参构造函数声明point类变量(类对象)pt1                         	pt1.print();        //输出pt1的信息
        return 0;
 }

 

构造函数能够进行重载:

一旦程序员为一个类定义了构造函数,编译器便不会为类自动生成缺省构造函数,

因此,如果还想使用无参的构造函数,如“point pt1;”的形式必须在类定义中显式定义一个无参构造函数。这样,构造函数就会出现两个,会不会有问题呢?不会,构造函数支持重载,在创建对象时,根据传递的具体参数决定采用哪个构造函数。

例1:

#include <iostream>
using namespace std;

class Point
{
public:
	Point()//构造函数能够进行重载
	{
        	cout << "Point()" << endl;
	} 

	Point(int ix, int iy)
	{
        	cout << "Point(int,int)" << endl;

        	_ix = ix;
        	_iy = iy;
	}

	void print()
	{

		cout << "(" << _ix<< "," << _iy<< ")" << endl;
	}

private:
        int _ix;
        int _iy;
};

int main(void)
{
        Point p1;//调用默认构造函数
        p1.print();

        Point p2(3, 4);
        p2.print();

        return 0;
}

例2

//point1.h
#include <iostream>
using namespace std;
class point //point类定义,在定义同时实现其成员函数
{
public:
        point(int x, int y)//有参构造函数
        {
                cout << "有参构造函数的调用" << endl;
                xPos = x;
                yPos = y;
        }
        point() //无参构造函数
        {
                cout << "无参构造函数的调用" << endl;
                xPos = 0;
                yPos = 0;
        }
        void print()//输出信息
        {
                cout << "xPos: " << xPos << ",yPos: " << yPos << endl;
        }
private: //私有成员,分泌诶代表x轴和y轴坐标
        int xPos;
        int yPos;
};
#include "point1.h"
int main()
{
        point pt1(3, 4);    //调用有参构造函数声明point类变量(类对象)pt1                         pt1.print();        //输出pt1的信息
        point pt2;            //调用无参构造函数声明point类变量(类对象)pt2                         pt2.print();        //输出pt2的信息
        return 0;
}

  

------------------

构造函数允许按参数缺省方式调用

上例中的构造函数可以作如下定义:

point(int x=0,int y=0)
{
        cout<<"对象创建时构造函数被自动调用"<<endl;
        xPos=x;
        yPos=y;
} 

此时,可在创建对象时省略参数,下列语句都是合法的:

 	point pt;    //x和y都采用默认值0
        point pt(3);    //x为3,y采用默认值0
        point pt(3,4);//x为3,y为4,两个参数都不采用默认值

初始化表达式1

除了在构造函数体内初始化数据成员外,还可以通过成员初始化表达式来完成。

成员初始化表可用于初始化类的任意数据成员(后面要介绍的static数据成员除外),

该表达式由逗号分隔的数据成员表组成,初值放在一对圆括号中。

只要将成员初始化表达式放在构造函数的头和体之间,并用冒号将其与构造函数的头分隔开,便可实现数据成员表中元素的初始化

point(int x,int y)
{
        cout<<"有参构造函数的调用"<<endl;
        xPos=x;
        yPos=y;
}
//等价于:
point(int x,int y)
:xPos(x)
,yPos(y)
{
        cout<<"有参构造函数的调用"<<endl;
}

--------------------------

初始化表达式2

每个成员在初始化表中只能出现一次

初始化的顺序不是由成员变量在初始化表中的顺序决定的,

而是由成员变量在类中被声明时的顺序决定的。(即在private中声明的顺序)

理解这一点有助于避免意想不到的错误。

//point.h
#include <iostream>
using namespace std;
class point
{
private:
        int yPos;    //先定义
        int xPos;    //后定义
public:
        point(int x)
        :xPos(x)
        , yPos(xPos) //初始化表取决于成员声明的顺序
        //如果换成
        //:yPos(y)
        //,x(yPos)//这样的话,x先初始化,但是这时yPos还没有初始化,x就是不确定的值了
        {
        }
        void print()
        {
                cout << "xPos: " << xPos << ", yPos: " << yPos << endl;
        }
};
#include "point.h"
int main()
{
        point pt1(3);        //调用有参构造函数声明变量pt1
        pt1.print();
        return 0;
}

  

例子:

#include <iostream>
using namespace std;

class Point
{
public:
#if 0
    Point()//构造函数能够进行重载
    {
        cout << "Point()" << endl;
    }
#endif

    Point(int ix, int iy)
    : _ix(ix)//初始化列表
    , _iy(iy)
    {
        cout << "Point(int,int)" << endl;
        //_ix = ix;//赋值
        //_iy = iy;
    }
    void print()
    {
        cout << "(" << _ix
             << "," << _iy
             << ")" << endl;
    }
private:
    int _ix;
    int _iy;
};

int main(void)
{
    Point p1;//调用默认构造函数
    p1.print();

    Point p3(5);
    p3.print();

    Point p2(3, 4);
    p2.print();

    return 0;
}

  

时间: 2024-10-07 05:35:55

9——对象的创建和撤销,构造函数和析构函数的相关文章

关注C++细节——含有本类对象指针的类的构造函数、析构函数、拷贝构造函数、赋值运算符的例子

本例只是对含有本类对象指针的类的构造函数.析构函数.拷贝构造函数.复制运算符使用方法的一个简单示例,以加深对构造函数和拷贝控制成员的理解. 读C++ primer 5th 第13章后加上自己的理解,完整的写了下课后习题的代码. 第一版: #include <string> #include <iostream> using namespace std; class TreeNode{ private: string value; TreeNode *left; TreeNode *

【c++总结-类】一个例子知道类的创建,对象,函数实现,构造函数,析构函数

例子: #include <iostream> using namespace std; class Person { public://类函数和成员函数都是public的,供外界调用 Person();//无参构造函数,如果没有构造函数会自动创建一个无参构造函数 Person(string name, int age);//有参构造函数 ~Person();//析构函数,释放内存 string getName();//name的getter方法 int getAge();//age的gett

2016.8.07 this、new、模式工厂、创建新的构造函数

写在前面:今天我将发表函数基础.对象基础.this.new.封装.封装例子  这几篇个人理解文章.最好能一口气看完,并自己写下,相信会对想要理解函数和对象的朋友们会有一些帮助. 目录:模式工厂. this.new.创建新的构造函数 模式工厂:将对象放入函数中,批量创建对象 代码: function Student(name,age,sex){ var o=new Object();//先定义一个对象, o.name=name; o.age=age; o.sex=sex; o.sayHi=func

你好,C++(33)对象生死两茫茫 6.2.3 一个对象的生与死:构造函数和析构函数

6.2.2  使用类创建对象 完成某个类的声明并且定义其成员函数之后,这个类就可以使用了.一个定义完成的类就相当于一种新的数据类型,我们可以用它来定义变量,也就是创建这个类所描述的对象,表示现实世界中的各种实体.比如前面完成了Teacher类的声明和定义,就可以用它来创建一个Teacher类的对象,用它来表示某一位具体的老师.创建类的对象的方式跟定义变量的方式相似,只需要将定义完成的类当作某种数据类型,用之前我们定义变量的方式来定义对象,而定义得到的变量就是这个类的对象.其语法格式如下: 类名

C++:派生类的构造函数和析构函数

4.2 派生类的构造函数和析构函数4.2.1 派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数:当撤销派生类对象时,则先执行派生类的派生类的析构函数,随后再执行基类的析构函数. //例4.5 派生类的构造函数和析构函的执行顺序 #include<iostream> using namespace std; class Base{ //声明基类Base public: Base() { cout<<"Co

构造函数和析构函数的作用[转]

千万不要把构造函数和析构函数同普通的函数等同起来看待. 给你一段介绍,这个是一位真正的高手的回答,看了你就会明白的.第9章         类的构造函数.析构函数与赋值函数构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹大意,其实这些貌似简单的函数就象没有顶盖的下水道那样危险.         每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A产生四

C#中构造函数和析构函数的用法

构造函数与析构函数是一个类中看似较为简单的两类函数,但在实际运用过程中总会出现一些意想不到的运行错误.本文将较系统的介绍构造函数与析构函数的原理及在C#中的运用,以及在使用过程中需要注意的若干事项.一.构造函数与析构函数的原理 作为比C更先进的语言,C#提供了更好的机制来增强程序的安全性.C#编译器具有严格的类型安全检查功能,它几乎能找出程序中所有的语法问题,这的确帮了程序员的大忙.但是程序通过了编译检查并不表示错误已经不存在了,在“错误”的大家庭里,“语法错误”的地位只能算是冰山一角.级别高的

构造函数与析构函数的起源

作为比 C 更先进的语言,C++提供了更好的机制来增强程序的安全性.C++编译器 具有严格的类型安全检查功能,它几乎能找出程序中所有的语法问题,这的确帮了程序 员的大忙. 但是程序通过了编译检查并不表示错误已经不存在了,在 "错误"的大家庭 里, "语法错误"的地位只能算是小弟弟.级别高的错误通常隐藏得很深,就象狡猾的罪 犯,想逮住他可不容易. 根据经验,不少难以察觉的程序错误是由于变量没有被正确初始化或清除造成的, 而初始化和清除工作很容易被人遗忘. Strous

详解~实现Runnable方法创建线程之为什么要将Runnable接口的子类对象传递给Thread的构造函数

/** * @author zhao * @TIME 0419 22:56  End *定义线程的第二种方法:实现Runnable接口 *步骤:1,定义一个子类实现Runnable接口 *    2,在子类中覆盖run()方法,并且将多线程锁执行的代码写入run方法中 *    3,通过Thread类建立线程对象: *    4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数.  为什么要将Runnable接口的子类对象传递给Thread的构造函数.  因为,自定义的