C++类和对象(1):构造函数和析构函数

一.类与对象的基本概念

1.结构体与类

C++提供了一种比结构体类型更安全有效的数据类型——。并且用class取代struct。

在C++中将类的成员分为两类:私有成员(用private说明)和公有成员(用public说明)。私有成员(包括数据成员和成员函数)只能被类内的成员函数访问,而不能被类外的对象访问;公有成员(包括数据成员和成员函数)既可被类内的成员函数访问,也可被类外的对象访问。但在C++中(即没有指定属于私有或公有时),类内的成员是私有的

代码解释如下:
 

#include<iostream>
#include<cmath>
using namespace std;
class Complex
{
	private:
		double real,imag;
	public:
	    void init(double r,double i)
		{
			real=r;
			imag=i;
		}
		double abs()
		{
			double t;
			t=real*real+imag*imag;
			return sqrt(t);
		} 		 
};
int main()
{
	Complex A;
	A.init(1.1,2.2);
	cout<<A.abs()<<endl;
	return 0;
}

在所示代码中,类外的对象A是无法对类内的私有成员进行直接访问。所以主函数中的代码语句
    A.init(1.1,2.2);
    cout<<A.abs()<<endl;

在编译时是错误的。

类类型的声明为:

class 类名
{

private:

          私有成员和成员函数

public:
           公有成员和成员函数

};

除了private和public之外,类中的成员还可以用另一个关键字protected来说明。被protected说明的数据成员和成员函数称为保护成员

2.成员函数

成员函数可以访问本类中的任何成员。

成员函数在类外定义的一般形式为:
返回值类型 类名::成员函数名(参数表)

{

函数体

}

注:

(1)在类的声明中,成员函数的原型的参数表中可以不说明参数的名字,而只说明它们的类型。如:

void setpoint(int,int);

但是,在类外定义成员函数时,不但要说明参数表中的参数类型,还必须要指出其参数名

(2)成员函数还可以直接定义在类的内部。如:

class Point
{
    public:
        void setpoint(int a,int b)         //成员函数setpoint直接定义在类的内部
        {
            x=a;
            y=b;
        }
        int getx()                         //成员函数getx直接定义在类的内部
        {
            return x;
        }
        int gety()                         //成员函数gety直接定义在类的内部
        {
            return y;
        }
    private:
        int x,y;
};

当然使用内联函数会减少函数调用的开销。定义函数的代码如下:

class Point
{
	public:
		inline void setpoint(int,int);                   //声明成员函数setpoint为内联函数 
	private:
		int x,y;
};
inline void Point::setpoint(int a,int b)                //在类外定义此函数为内联函数 
{
	x=a;
	y=b;
}

 3.对象的定义及使用

C++中的类的对象是对该类实例化的结果。

(1)对象的定义

有两种定义方式:

a.在声明类的同时,直接定义对象,即在声明类的右花括号“}”后。例如:

class Point
{
	public:
		void setpoint(int,int); 
		int getx();
		int gety();                  
	private:
		int x,y;
}op1,op2;                  //直接定义了对象op1和op2

 b.声明了类之后,在使用时再定义对象。基本格式如下:

类名 对象名1,对象名2,······;

例如:

Point op1,op2;

(2)对象中成员的访问

(a)通过对象名和对象选择符访问对象中的成员

一般形式为:

对象名.数据成员名

对象名.成员函数名[(实参表)] 

其中“.”叫做对象选择符,简称点运算符。

(b)通过指向对象的指针访问对象中的成员

例:

Date d,*ptr;

ptr=*d;

使用d.year或(*ptr).year或ptr->year三者等价。

(c)定义一个引用,通过引用来访问对象中的成员,其方法跟通过对象名来访问对象中的成员是相同的。

二.构造函数和析构函数

1.对象的初始化和构造函数

class Complex

{
double real=0.0;                            //在类声明中不能给数据成员赋初值

}

在定义对象时,对数据成员赋初值称为对象的初始化。

如果一个类的对象都是公有的,则可以在定义对象时对数据成员进行初始化。若类中包含私有的或者保护的成员,就不能用这种方法进行初始化。

所以一般用构造函数对数据进行初始化。构造函数的名字必须与类名相同他可以有任意类型的参数,但不可有返回值类型

构造函数的主要功能是对对象进行初始化,即对数据成员赋值。

在建立对象的同时,采用构造函数给数据成员赋值,通常采用以下两种形式:

(1)类名 对象名[(实参表)];

例:

complex(double r,double i)

{

real=r;

imag=i;

}
类型一:
complex A(1.1,2.2);

(2)类名*指针变量名=new类名[(实参表)];

这是一种用new运算符动态建立对象的方式。

说明:

(1)构造函数无返回值,在定义构造函数时,不能说明其类型。

(2)构造函数即可以定义在类内,也可以定义在类外。

定义在类外的情况如下:

Complex::Complex(double r,double i)
{
real=r;
imag=i;
}

(3)构造函数的作用主要是用来对对象进行初始化

(4)构造函数一般声明为公有成员,但它不需要也不能像其他成员函数一样被显式调用,他是在定义对象时被自动调用的,且只执行一次

(5)在实际应用中,通常要给每个类定义构造函数。如果没有定义构造函数,系统就会自动的生成一个默认的构造函数。

(6)构造函数还可以不带参数。如:

class Complex
{
    public:
        Complex()
        {
        real=0;
        imag=0;
        }
    private:
        double real,imag;
};

 2.用成员初始化列表对数据成员进行初始化

在声明类中,对数据成员的初始化工作一般是在构造函数中用赋值语句进行。

如:

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r,double i);
};
Complex::Complex(double r,double i)
{
	real=r;
	imag=i;	
} 

还有另一种方式——用成员初始化列表来实现对数据成员的初始化。如:

class Complex
{
	private:
		double real,imag;
	public:
		Complex(double r,double i);
};
Complex::Complex(double r,double i):real(r),imag(i)
{
}

带有初始化成员列表的构造函数的一般形式如下:
类名::构造函数名([参数表])[:(成员初始化列表)]

{

//构造函数体

}

成员初始化列表一般形式为:
数据成员名1(初始值1),数据成员名2(初始值2),······

对于const修饰的数据成员,或者是引用类型的数据成员,是不允许使用赋值语句直接赋值的。只能用成员的初始化列表对其进行初始化。

如:

#include<iostream>
using namespace std;
class A
{
	public:
		A(int x1):x(x1),rx(x),pi(3.14)
		{
		}
		void print()
		{
			cout<<"x="<<x<<"rx="<<rx<<"pi="<<pi<<endl;
		}
	private:
		int x;
		int& rx;
		const double pi;
};
int main()
{
	A a(10);
	a.print();
	return 0;
}

输出结果为:

x=10 rx=10 pi=3.14

数据成员是按照他们在类的声明中的顺序来进行初始化的,与他们在成员初始化列表中列出的顺序无关

3.构造函数的重载

C++允许构造函数重载,以便适应不同的场合。例:

#include<iostream>
using namespace std;
class Date
{
	public:
		Date();
		Date(int y,int m,int d);
		void showDate();
	private:
		int year;
		int month;
		int day;
};
Date::Date()
{
	year=2000;
	month=4;
	day=28;
}
Date::Date(int y,int m,int d):year(y),month(m),day(d) 
{
}
inline void Date::showDate()
{
	cout<<year<<"."<<month<<"."<<day<<endl; 
}
int main()
{
	Date date1;
	cout<<"Date1 output:"<<endl;
	date1.showDate();
	Date date2(2002,11,14);
	cout<<"Date2 output:"<<endl;
	date2.showDate();
	return 0;
}

运行结果如下:

注:

使用无参构造函数创建对象时,应该用语句“Date date1;”,而不能用语句“Date date1();”,因为语句“Date date1();”表示声明一个名为date1的普通函数,此函数的返回值为Date类型

4.带默认参数的构造函数

例:

#include<iostream>
#include<cmath>
using namespace std;
class Complex
{
	public:
		Complex(double r=0.0,double i=0.0);
		double abs();
	private:
	    double real,imag; 
};
Complex::Complex(double r,double i)
{
	real=r;
	imag=i;
}
double Complex::abs()
{
	double t;
	t=real*real+imag*imag;
	return sqrt(t);
}
int main()
{
	Complex s1;                               //没有传递实参,全用默认值
	cout<<"s1"<<s1.abs()<<endl;
	Complex s2(1.1);                          //传递了一个实参
	cout<<"s2"<<s2.abs()<<endl;
	Complex s3(1.1,2.2);                      //传递了两个实参
	cout<<"s3"<<s3.abs()<<endl;
	return 0;
}

说明:
(1)如果构造函数在类的声明外定义,那么默认参数应该在类的声明构造函数原型时指定,而不能在类外构造函数定义时指定

(2)如果构造函数的全部参数都指定了默认值,那么这时的构造函数也属于默认构造函数

(3)在一个类中定义了全部是默认参数的构造函数时,不能再定义重载构造函数

4.析构函数

析构函数常用于执行一些清理任务,如释放内存的分配空间

注:

(1)析构函数在他前面必须加一个波浪号(~)

(2)析构函数不返回任何值。不能说明其类型

(3)析构函数没有参数,不能被重载,一个类只能有一个析构函数

说明:

(1)每个类必须有一个析构函数

(2)除了在主函数结束之后,对象被撤销时,析构函数会被自动调用外,还有两种情况会被调用。

其一:如果一个对象被定义在函数体内,当这个函数调用结束之后,该对象会被释放,析构函数自动调用。

其二:若一个对象是使用new运算符动态创建的,则用delete来释放它时,delete会自动调用构造函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值