构造函数
一、构造函数的作用
➢构造函数是类中的特殊成员函数。
➢构造函数的作用是完成对象的初始化。
➢给出类定义时,由程序员编写构造函数。如果程序员没有编写类的任何构造函数,则由系统自动添加一个不带参数的构造函数。
二、构造函数的定义
➢构造函数在类体里的声明形式:
类名 (形参1,形参2,···形参n) ; //也可没有形参
➢构造函数的定义形式:
假设数据成员为X1, X2,···Xn,类体外定义构造函数时通常有
3种形式:
①类名::类名(形参1,形参2, ···形参n) :X1 (形参1),X2 (形参2), Xn(形参n) { }
②类名::类名(形参1形参2,···,形参n)
{ X1=形参1;
X2=形参2;
···
Xn=形参n;
}
③类名::类名( ) //成员变量所赋的初值都是固定的
{ X1 =初始化表达式1;
X2 =初始化表达式2;
···
Xn=初始化表达式n;
}
说明:
①构造函数的名字必须和类名相同;
②在定义构造函数时不能指定返回类型,即不要返回值,即使是void类型也不可以;
③另外类可有多个构造函数,即函数重载;或重载
④构造函数的参数在排列时无顺序要求,只要保证相互对应即可;
⑤构造函数可以使用默认参数。
⑥在程序中说明一一个对象时,程序自动调用构造函数来初始化该对象。
三、构造函数的使用
➢当程序创建一一个对象时,系统自动调用构造函数来初始化该对象。
例使用构造函数创建类的对象
myDate d( );//使用无参的构造函数
myDate d1(25);//使用1个参数的构造函数
myDate d2(10,20);
myDate d3(1971,9,18);
对象d的值: 1970/1/1, 对象d1的值: 1970/1/25
对象d2的值: 1970/10/25, 对象d3的值: 1971/9/18
例使用构造函数的默认参数创建对象
myDate::myDate(int y=1970, int m=2, intd=14)
{ year=y; month=m; day=d;
myDate d( );//使用无参的构造函数
myDate d1( 1980);//使用1个参数的构造函数
myDate d2(1990,3);
myDate d3(2000,4,18);
对象d的值: 1970/2/14, 对象d1的值: 1980/2/14
对象d2的值: 1990/3/14, 对象d3的值: 2000/4/18
注意:
使用new创建对象时,下面两种都是合法的:
myDate *pd = new myDate( ) //带括号
myDate *pd = new myDate//不带括号
➢用户定义了构造函数,都会调用构造函数进行初始化;
➢用户未定义构造函数,对带括号的情况,系统在为成员变量分配内存的同时,将其初始化为0。不加括号
时,系统只为成员变量分配内存空间,但不进行内存;的初始化,成员变量的值是随机值。
四、复制构造函数与类型转换构造函数
1.复制构造函数
➢复制构造函数是构造函数的一种,也称为拷贝构造函数。
➢复制构造函数的作用:使用一个已存在的对象去初始化另一个正在创建的对象
➢复制构造函数其原形为:
类名::类名(类名&) //对象 的引用作为形参
或 类名::类名(const 类名&) //为了不改变原有对象,使用const限制。
➢如果类中没有给出复制构造函数,那么编译器会自动生成一个默认复制构造函数。
例调用复制构造函数
Student stud;
Student ss[2] = { stud, Student(};//创建ss[0]中的对象时,用到了默认复制构造函数。
第二条语句也可以写为如下的三条语句:
Student ss[2];
ss[0] = Student(stud); //调用默认复制构造函数
ssS[1] = Student();//调用构造函数
➢声明和实现复制构造函数的一般格式:
class 类名
{ public:
类名 (形参表) ; //构造函数
类名 (类名 &对象名) ; //声明复制构造函数
......
};
类名::类名(类名 &对象名) //复制构造函数的实现
{ 函数体
}
2.类型转换构造函数
只有一个参数的构造函数(复制构造函数除外)都可以称为类型转换构造函数。类型转换构造函数能够使一个其他类型的变量或常量自动转换成-一个临时对象。类型转换构造函数实现的是从参数类型到该类类型的转换,函数带一个参数。
析构函数
➢析构函数和构造函数、复制构造函数都是构造型成员函数的基本成员;
➢析构函数的作用是在对象消失时,释放由构造函数分配的内存;
➢析构函数在类体里的声明形式: ~类名() ;
➢析构函数的定义形式: 类名::~类名() {}
➢类只能定义一个析构函数,且不能指明参数 ;
➢如果程序中没有定义析构函数,则编译器自动生成默认的析构函,数默认析构函数的函数体为空,
➢使用new运算符动态分配了内存空间,则在析构函数中应该使用delete释放掉这部分占用的空间。
➢当程序先后创建几个对象时,系统按照后建先析构的原则析构对象,当使用delete调用析构函数时,则按delete的顺序析构。
➢析构函数在对象的生存期结束时被编译系统自动调用,然后对象占用的内存被回收。,
类的静态成员
一、静态变量
➢静态变量根据变量定义的位置不同,分为静态全局变量和静态局部变量。
➢静态全局变量: static修饰的、 在所有花括号之外声明的变量,其作用域范围是全局可见的,即在整个项目文件内都有效。
➢静态局部变量: static修饰的、 块内定义的变量,其作用域从定义之处开始到本块结束处为止。
➢静态变量均存储在全局数据区,静态局部变量只执行一次初始化。如果静态变量未初始化,则系统将其初始化为0。
二、类的静态成员
➢类的静态成员有两种:静态成员变量和静态成员函数。
➢定义静态成员:在类体内定义成员时,在前面加上static。
➢静态成员变量不能在类体内赋值。给静态成员变量赋初值的格式
如下:
类型 类名::静态成员变量=初值; //不能有static
➢在类体外定义成员函数时,前面也不能加static。
➢类的静态成员被类的所有对象共享。
➢静态函数与静态函数之间、非静态函数与非静态函数之间是可以相互调用的,非静态成员函数内可以调用静态成员函数,但静态成员函数内不能调用非静态成员函数。
➢访问类静态成员的一般格式如下:
类名::静态成员名
对象名静态成员名
对象指针->静态成员名
静态成员函数与一般成员函数的不同
(1)可以不指向某个具体的对象,只与类名连用;
(2)在没有建立对象之前,静态成员就已存在;
(3)静态成员是类的成员,不是对象的成员;
(4)静态成员为该类的所有对象共享,它们被存储于一个公用内存中;
(5)没有this指针,只能通过对象名或指向对象的指针访问类的数据成员;
(6)静态成员函数不能被说明为虚函数;
(7)静态成员函数不能直接访问非静态函数
静态对象与普通对象区别
(1)静态对象的构造函数在代码执行过程中,在第一次遇到它的变量定义并初始化时被调用,但直到整个程序结束之前仅调用一次;而普通对象则是遇到变量定义就被调用,遇到几次调用几次。
(2)静态对象的析构函数在整个程序退出之前被调用,同样也只调用一次;而普通对象则是变量被定义几次,则析构几次。
变量及对象的生存期和作用域
一、变量的生存期和作用域
➢变量的生存期:是指变量所占据的内存空间由分配到释放的时期。
➢变量的作用域:变量有效的范围。
➢全局变量:是程序中定义在所有函数之外的变量。其作用域是程序从变量定义到整个程序结束的部分;其生存期为整个程序的执行期间。
➢局部变量:在函数内或程序块内说明的变量。其作用域是函数体内或程序块内(一-对大括号括起来的程序段)其生存期为从被说明处开始,到所在函数或程序块结束处结束。
二、类对象的生存期和作用域
➢类的对象在生成时调用构造函数,在消亡时调用析构函数,在这两个函数调用之间即是对象的生存期。
➢类的对象的作用域和变量的作用域含义一-致。
常量成员和常引用成员
常量成员和常引用成员
1、类常量成员变量
➢类常量成员变量:由关键字const修饰的类成员变量。
➢定义类常量成员变量的一般格式如下:
const
数据类型 类常量成员变量 = 表达式;
➢类常量成员变量必须进行初始化,而且只能通过构造函数的成员初始化列表的方式进行。
2、常量对象
➢常量对象:使用const声明的对象就是常对象
➢常量对象必须在声明的同时进行初始化,而且不能被更新。
定义常量对象的一般格式:
const类名对象名(参数表) ; 或
类名const对象名(参数表) ;
例: myDate const al(1997,12,20); //定义myDate类常量对象al并初始化
➢常量对象只能调用常量成员函数,不能调用非常量函数,普通对象可以调用所有成员函数
3、常量函数
➢常量函数:用const声明、 定义的成员函数。
➢常量函数的声明及定义形式为:
(1)在类体内定义常量函数为内联函数时的形式: 类型标识符 函数名 (参数列表) const {....函数体}
(2)在类体内声明,类体外定义时的形式
声明形式:类型标识符 函数名 (参数列表) const;
定义形式:类型标识符 类名::函数名 (参数列表) const {....//函数体}
➢常量函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
4、常引用作为函数的参数
使用引用作为函数参数,传送的是地址,所以形参改变,则实参也跟着改变,但如果不希望函数改变对象的值,就要使用常引用作为参数,
例如: void Display(const double& r) {cout<< r< <endl;} //Display只能使用而不能改变r所引用的对象。
成员对象和封闭类
成员对象和封闭类的概念
➢一个类的成员变量如果是另一个类的对象,则该成员变量称为“成员对象”。 这两个类为包含关系。包含成员对象的类叫作封闭类。
一、封闭类构造函数的初始化列表
➢在定义封闭类的构造函数时,需要添加初始化列表,指明要调用成员对象的哪个构造函数。
➢在封闭类构造函数中添加初始化列表的格式如下:
封闭类名::构造函数名 (参数表) :成员变量1 (参数表),成员变量2 (参数表),...{...}
例: Student::Student(string n);:name(n), birthday(myDate(){}
➢执行封闭类的构造函数时,先执行成员对象的构造函数,然后再执行本类的构造函数。
➢封闭类对象生成时,先执行所有成员对象的构造函数,然后执行封闭类自己的构造函数。
➢成员对象构造函数的执行次序与成员对象在类定义中的说明次序一致,与它们在构造函数初始化列表中出现的次序无关。
➢当封闭类对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数,成员对象析构函数的执行次序和构造函数的执行次序相反,即先构造的后析构
友元
一、友元
➢友元是为了兼顾C语言程序设计的习惯与C++信息隐藏的特点,而特意增加的功能。
➢友元机制是对一-些类外的函数打开的一个特殊通道,授权它们能够访问本类的私有成员变量。
➢友元的概念破坏了类的封装性和信息隐藏,但有助于数据共享,能够提高程序执行的效率。
➢友元机制包括友元函数和友元类。
二、友元函数
➢友元函数:在定义一个类的时候,可以把一些函数(”包括全局函数和其他类的成员函数)声明为“友元“,这样那些函数就成为本类的友元函数。
➢在类定义中声明友元函数形式:
friend函数类型函数名(参数列表) ; //针对全局函数
friend函数类型函数所在类名::函数名(参数列表) ;
➢友元函数可在类中的私有或公有部分通过关键字friend声明或定义,但如在类中声明,而在类外定义,就不能再在类外使用friend关键字。
➢友元函数应被看作类的接口的一部分,使用它的主要目的是提高效率,因为它可以直接访问对象的私有成
员,从而省去调用类的相应成员函数的开销。
➢友元函数的另一一个优点是:类的设计者不必在考虑好该类的各种可能使用情况之后再设计这个类,
而是可以根据需要,通过使用友元来增加类的接口。
三、友元类
➢如果将一个类B说明为另一个类A的友元类,则类B中的所有函数都是类A的友元函数。
➢在类定义中声明友元类的格式如下:
friend class类名;
➢友元类的关系是单向的。若说明类B是类A的友元类,不等于类A也是类B的友元类。友元类的关系不能传递,即若类B是类A的友元类,而类C是类B的友元类,不等于类C是类A的友元类。
➢除非确有必要,一般不把整个类说明为友元类,而仅把类中的某些成员函数说明为友元函数。
this指针
一、this指针的概念和作用
➢C++规定,当一个成员函数被调用时,系统将自动向它传递一个隐含的参数,该参数是一个指向调用该函数的对象的指针,名为this指针,从而使成员函数知道该对哪个对象进行操作。
➢使用this指针,保证了每个对象可以拥有自己的数据成员,但处理这些数据成员的代码却可以被所有的对象共享,从而提高了程序的安全性和效率。
➢this指针 是C+ +实现封装的一种机制,它将对象和该对象调用的成员函数连接在-起,从而在外部看来,每个对象都拥有自己的成员函数。
➢this指针指向的是成员函数作用的对象,也就是调用成员函数的对象。友元函数不通过对象调用,所以没有this指针。
➢静态成员是类具有的属性,不是对象的特征,this表示的是隐藏的对象的指针,所以静态成员函数没有this指
针。