赞
踩
一、构造函数
1、利用构造函数完成对象的初始化
C++中构造函数是专门用来初始化对象的成员函数。它是一种特殊的成员函数,它不需要用户来调用,而是
在建立对象的时候自动调用。
说明:
1、构造函数在建立对象时被自动调用
2、构造函数名与类名相同,无返回值,一般声明为public
3、用户如果没有定义构造函数,则系统会自动生成一个。但是函数体为空,无参数、不执行任何初始化操作
(1)在构造函数的函数体内进行赋值操作
函数首部一般为:
构造函数名(类型1 形参1, 类型2 形参2, ........)
{
函数体内完成对象数据成员进行赋值操作;
}
定义对象时:
类名 对象名(实参1,实参2,.....)
using namespace std; #include <iostream> class Person{ public: Person(string name, char sex); void showPerson(); private: string name; char sex; }; Person::Person(string name, char sex) { //构造函数逇函数体内进行赋值操作 this->name = name; this->sex = sex; } void Person::showPerson() { cout << "name : " << name << endl; cout << "sex : " << sex << endl; } int main() { Person p1("lilei", 'M'); p1.showPerson(); return 0; } 运行结果: name : lilei sex : M
(2)用参数初始化列表完成对象的初始化
有些情况下不能通过在构造函数的函数体内进行赋值操作,必须利用构造函数的参数初始化列表对对象进行初
始化。必须用构造函数的参数初始化列表进行初始化的情况有如下几种:
1、含有const成员变量时必须有构造函数的参数初始化列表完成初始化
2、含有成员对象时必须在构造函数的参数初始化列表中指定用哪个构造函数完成对成员对象的初始化
3、在继承层次中,子类构造函数的参数初始化列表中必须指定调用父类的哪个构造函数完成对父类成员的初始化,
否则调用父类的无参构造函数对父类成员进行初始化。
(3)构造函数体内赋值和构造函数的参数初始化列表说明
1、从严格意义上来说,构造函数的执行分为两个阶段:首先执行构造函数的初始化列表,然后执行构造函数的函数
体赋值操作。
2、当使用构造函数的参数初始化列表对数据成员进行初始化时,类中所有的数据成员都会在执行构造函数的函数体
之前,通过构造函数的初始化列表完成初始化,无论该数据成员是否显式的出现在初始化列表中。
一般形式:
构造函数名(形参列表): 成员初始化列表
{
函数体;
}
如果类的数据成员是数组,则应当在构造函数的函数体内对其赋值,而不能在参数初始化列表中完成。
例如:
char name[10] = "lieli", char name2[10] = {0};
name2 = name;//错误用法
例如:
class Person{
public:
Person(char name[N], char sex, int age):sex(sex),age(age)
{
strcpy(name,name);
}
private:
char name[N];
char sex;
int age;
};
2、构造函数的重载
构造函数可以重载,一个类可以有多个构造函数。在创建对象时会根据传入参数自动调用相应的构造函数。
3、使用默认参数的构造函数
一个类只能有一个默认参数的构造函数,默认参数的构造函数即在定义对象时不需要传入实参的构造函数,
默认参数的构造函数有如下几种情况:
1)用户未定义构造函数时,由系统提供的无参构造函数
2)用户自定义的无参构造函数
3)有参构造函数的形参全部指定了默认值
如果定义对象时调用了无参构造函数,不需要给出实参。则应该注意定义对象时的格式:
Box box1;//定义对象,调用无参构造函数
Box box2();//错误,相当于定义一个函数;
4、拷贝构造函数
拷贝构造函数是一种特殊的构造函数,一般不用自己定义,会由系统提供的默认的拷贝构造函数。拷贝构造 函数不可以被重载,它有固定的格式。 默认的拷贝构造函数是按顺序对成员变量进行拷贝赋值的操作,也即浅拷贝;浅拷贝有些情况下并不适用, 因此特殊情况下需要自己定义拷贝构造函数,进行深拷贝。 拷贝构造函数的格式: 类名(const 类名& that) { 函数体; } 拷贝构造函数的使用场景: 1)使用同类对象初始化另一个同类对象时 Person p2 = p1; <==> Person p2(p1);//都调用拷贝构造函数 2)普通类对象作为函数形参时,实参与形参的虚实结合过程中调用拷贝构造函数 3)类对象作为函数返回值时,主调函数接收被调函数的返回值过程中调用拷贝构造函数 说明:因为调用拷贝构造函数需要开辟内存空间,比较消耗时间和空间,而一份相同的数据占用多个存储空间是 不合理的,因此通常用引用作为函数的形参和返回值,这样可以避免调用拷贝构造函数,提高效率。
using namespace std; #include <iostream> class Person{ public: Person(string name, char sex) : name(name), sex(sex){} //构造函数 Person(const Person& that){ //拷贝构造函数 this->name = that.name; this->sex = that.sex; } void showPerson() { cout << "name : " << name << endl << "sex : " << sex <<endl; } private: string name; char sex; }; //全局函数: void func(Person p)//实参与形参虚实结合过程中调用拷贝构造函数 { cout << "func()" << endl; } void func2(const Person& p)//引用作为函数形参 { cout << "func2()" << endl; } int main() { Person p1("lilei", 'M'); p1.showPerson(); Person p2(p1); // <==> Person p2 = p1;用一个类对象初始化同类的对象时调用拷贝构造函数 p2.showPerson(); Person p3; p3 = p1;//这里调用的是赋值运算符函数(运算符的重载),并未调用拷贝构造函数。 func(p1);//调用拷贝构造函数 func2(p1);//未调用拷贝构造函数 return 0; }
拷贝构造函数利用初始化列表的形式,向这种情况如果有const成员变量也需要重新定义拷贝构造函数。
二、析构函数
析构函数用于销毁对象前执行一些清理工作,释放资源等。析构函数与构造函数的作用相反,当对象的生
存期结束时,会自动调用析构函数。
析构函数与构造函数的调用顺序:
一般而言先构造的后析构,但析构的先后顺序不仅仅取决于构造的顺序,还取决于对象的存储类别,生命期。
静态存储区、动态存储区(堆区、栈区)。
总结:
1)先构造的后析构
2)根据存储类型及生存期来说,非静态局部变量、静态局部变量和全局变量、malloc或new获取的变量
三、无名对象与explict关键字
1、无名对象
直接调用构造函数创建的对象是无名对象,无名对象的声明周期是临时的,又叫做临时对象。它的声明周
期在该语句所在的行。
假设Person是已声明的类:
Person p1("Tom", 'M'); //类类型 变量名 =》定义了一个有名对象
Person("Tom, 'M'); //直接调用构造函数 =》 定义了一个无名对象
无名对象的用法:
Person p2; 优于:----》 Person p1("Tom", 'M');
p2 = Person("Tom", 'M'); p2 = p1;
例题:用一个无名对象去初始化一个同类的对象
2、explict关键字
explicit关键字可以用来修饰单参构造函数,禁止隐式的构造单参无名对象。在C++中常用explicit修饰
一个参数的构造函数(或者除了第一个参数外,其余参数都有默认值的多参构造函数),这时会禁止构造函数
进行隐式转换;否则explicit关键字不起作用。
using namespace std; #include <iostream> class Person{ public: explicit Person(string name) : name(name){} void showPerson(){ cout << "name : " << name << endl; } private: string name; }; int main() { //Person p1 = string("lilei");//隐式构造,当单参构造函数没有用explicit做声明时成立 //p1.showPerson(); Person p2 = Person(string("lilei");//显示构造 p2.showPerson(); return 0; }
四、const成员与对象
1、const成员
1、const数据成员:
const数据成员和普通的const成员变量类似,这里不做赘述。
2、const成员函数
一般形式: 返回值类型 函数名(形参列表) const;
通常将不打算修改成员变量的函数声明为const,不可以用于修饰普通函数;const修饰的成员函数不可
直接或者间接的修改成员变量,因此规定const成员函数不可以调用非const成员函数;同名同参的成员函数,
const与非const构成重载关系。
2、const对象
const对象只能调用const成员函数,不可以调用非const成员函数;非const对象会优先选择非const版本
的成员函数。
五、static静态成员
1、static静态成员变量
static静态成员变量用来描述本类中所有对象的某一共有属性。本类的所有对象的static数据成员都是同
一块内存,存放在静态存储区。
注意:
static成员变量在类内进行声明,在类外进行定义和初始化。变量的定义需要开辟内存,而类的定义是抽象的,
没有进行实例化并不会开辟内存空间。因此,只可在类内对static成员进行声明,在类外进行定义和初始化。
声明:static 类型 静态成员变量名
定义:类型 类名::静态成员变量 = 初值
class Person{
private:
static int counter;//静态成员变量的声明
};
int Person::counter = 0;//类外进行静态成员变量的定义和初始化
注意:
在建立对象时不可以通过初始化列表对静态成员变量进行初始化(变量只能初始化一次,static成员变量是该
类的公有属性,如果每个对象都在初始化列表中对静态成员变量进行了初始化则会矛盾),如果想改变静态成员变
量的值,可以在构造函数的函数体内实现。静态成员变量如果为private时,不可以在类外进行访问,仍要通过类
的成员函数才可以访问。
2、静态成员函数
一般形式: static 返回值类型 函数名(形参列表)
静态成员函数用于访问静态成员变量;静态成员函数可以通过对象名调用或者类名进行调用。
静态成员函数与非静态成员函数的区别:
非静态成员函数有this指针,而静态成员函数没有。因此静态成员函数无法访问非静态成员变量。
using namespace std; #include <iostream> class Student{ public: static void showCounter(){ cout << "counter : " << counter << endl; }//静态成员函数用于访问静态数据成员,而不可以访问非静态数据成员 private: static int counter;//静态成员变量在类内进行声明 }; int Student::counter = 0;//静态成员变量在类外进行定义和初始化 int main() { Student stu1; stu1.showCounter();//通过类对象调用静态成员函数 Student::showCounter();//通过类名和域名限定符::来调用静态成员函数 return 0; }
六、this指针
1、概念: (1)类的非静态成员函数都有一个隐藏的形参名为this,类型是 " T*",类指针 (2)this指针的作用域是非静态成员函数的函数体 (3)this指针保存的是当前对象的地址 (4)对于构造函数,this指针指向正在被创建的对象。 2、this指针的引用: (1)避免名字冲突: Person (string name, char sex, int age) { this->name = name; this->sex = sex; this->age =age; } (2)返回调用对象本身的地址: 类名* func() 类名& func() { { return this; return *this; } }
七、成员对象的构造与析构
1)成员对象的初始化由成员对象的构造函数负责
2)成员对象的构造在当前对象的构造之前,成员对象的析构在当前对象之后。
3)多个成员对象的构造顺序与成员对象在类中定义的先后顺序相关,与成员对象在初始化列表中的顺序无关。
含有成员对象的类的构造函数一般形式:
类的构造函数名(总形参数表):成员对象名(实参表)
{
其它数据成员的初始化;
}
说明:
1)对成员对象的初始化只能在初始化列表中完成。
2)含有成员对象的类对象的一次初始化过程需要两次调用成员对象的构造函数或者拷贝构造函数。
using namespace std; #include <iostrema> class People{ public: //People的构造函数 People(string name, char sex):name(name),sex(sex) { cout << "People的构造函数" << endl; } //People的拷贝构造函数 People(const People& that) { this->name = that.name; this->sex = that.sex; cout << "People的拷贝构造函数" << endl; } private: string name; char sex; }; class Student{ public: //以下是Student类的构造函数的两种不同形式 Student(int number, People p1):number(number),p1(p1) { cout << "Student(int number, People p1)" << endl; }//至少一次调用成员对象的拷贝构造函数 Student(int nunber, string name, char sex):number(number),p1(name, sex) { cout << "Student(int number, string name, char sex)" << endl; }//调用一次成员对象的构造函数 private: int number; People p1;//成员对象 }; int main() { People p("wanghao",'M'); Student stud1(1001, p);//调用两次成员对象Person的拷贝构造函数;1)对象p去初始化Student的 //构造函数总参数列表;2)Student构造函数总参数列表中的p1区初始化成员对象p1 cout << "==================================" << endl; //说明: //People p1 = People("lilei", 'M'); //使用无名对象去初始化另一个同名的对象只会调用一次People类的含参构造函数,并不会调用People //的拷贝构造函数 Student stud2(1001, People("wanghao",'M'));//成员对象的构造函数和拷贝构造函数分别调用一次 //1)无名对象People("wanghao", 'M')去初始化Student总参数列表中的p1,调用一次成员对象的构造 //函数;2)Student构造函数总参数列表中的p1去初始化成员对象p1,调用成员对象的拷贝构造函数 cout << "===================================" << endl; Student stud3(1001,string("wanghaoo"),'M');//只调用一次成员对象的构造函数;在Student的 //构造函数中用总形参列表中的name和char去初始化成员对象p1 return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。