构造函数
构造函数主要用来在创建对象时完成对对象属性的一些初始化等操作, 当创建对象时, 对象会自动调用它的构造函数。
一般来说, 构造函数有以下三个方面的作用:
- 给创建的对象建立一个标识符
- 为对象数据成员开辟内存空间
- 完成对象数据成员的初始化
构造函数不能被显示的调用。
-
初始化列表与赋值
C++类中成员变量的初始化有两种方式:构造函数初始化列表和构造函数体内赋值。
注:列表初始化时成员变量初始化的顺序是按照在类中定义的顺序。
主要有以下几种情况:
-
内部数据类型(int, char, 指针等)
两者初始化方法效率基本没区别
-
无默认构造函数的继承关系中
class Animal { public: Animal(int weight,int height): //没有提供无参的构造函数 m_weight(weight), m_height(height) { } private: int m_weight; int m_height; }; class Dog: public Animal { public: Dog(int weight,int height,int type) //error 构造函数 父类Animal无合适构造函数 { } private: int m_type; };
这种必须在派生类构造函数中初始化提供父类的初始化
class Dog: public Animal { public: Dog(int weight,int height,int type): Animal(weight,height) //必须使用初始化列表增加对父类的初始化 { ; } private: int m_type; };
-
类中const常量,必须在初始化列表中初始,不能使用赋值的方式初始化
-
包含有自定义数据类型(类)对象的成员初始化
构造函数初始化列表的方式得到更高的效率
-
-
构造函数的重载
-
当用户自定义构造函数后编译器就不会再为对象生成默认构造函数
-
当重载具有默认参数的构造函数时需防止出现二义性
例如:编译器会报 error: call of overloaded 'Point()' is ambiguous
Point(int x = 0, int y = 0) //默认参数的构造函数 { xPos = x; yPos = y; } Point() //重载一个无参构造函数 { xPos = 0; yPos = 0; }
-
析构函数
与构造函数相反, 析构函数是在对象被撤销时被自动调用, 用于对成员撤销时的一些清理工作, 例如在前面提到的手动释放使用 new 或 malloc 进行申请的内存空间。
析构函数具有以下特点:
- 析构函数函数名与类名相同, 紧贴在名称前面用波浪号 ~ 与构造函数进行区分, 例如: ~Point()
- 构造函数没有返回类型, 也不能指定参数, 因此析构函数只能有一个, 不能被重载
- 当对象被撤销时析构函数被自动调用, 与构造函数不同的是, 析构函数可以被显式的调用, 以释放对象中动态申请的内存。
当用户没有显式定义析构函数时, 编译器同样会为对象生成一个默认的析构函数, 但默认生成的析构函数只能释放类的普通数据成员所占用的空间, 无法释放通过 new 或 malloc 进行申请的空间, 因此有时我们需要自己显式的定义析构函数对这些申请的空间进行释放, 避免造成内存泄露。
this指针
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
静态成员变量及静态成员函数
对象之间的成员变量是相互独立的,如果要共享数据,则需使用静态成员变量和成员函数。
-
内存分配
静态成员是在程序编译时分配空间,而在程序结束时释放空间。
-
声名及定义
static关键字只能用于类定义体内部的声明中,初始化时不能标示为static。
初始化静态成员变量要在类的外面进行。
初始化的格式如下:数据类型 类名::静态成员变量名 = 初值。
-
使用
静态成员可以通过双冒号来调用,即<类名>::<静态成员名>。
也可以通过对象名来对静态成员变量进行引用<对象>.<静态成员名>。
类的静态成员变量必须先初始化再使用。 -
区别
普通成员函数和静态成员函数的区别是:普通成员函数在参数传递时编译器会隐藏地传递一个this指针.通过this指针来确定调用类产生的哪个对象;但是静态成员函数没有this指针,不知道应该访问哪个对象中的数据,所以在程序中不可以用静态成员函数访问类中的普通变量。
静态成员函数不能访问非静态成员,但可以访问静态成员。
非静态成员函数可以访问静态成员。
const成员变量与成员函数
在类中,如果你不希望某些数据被修改,可以使用const
关键字加以限定。const
可以用来修饰成员变量和成员函数。
-
const成员变量
const 成员变量的用法和普通 const 变量的用法相似,只需要在声明时加上 const 关键字。初始化 const 成员变量只有一种方法,就是通过构造函数的初始化列表。
-
const成员函数
const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值,这种措施主要还是为了保护数据而设置的。const 成员函数也称为常成员函数。
常成员函数需要在声明和定义的时候在函数头部的结尾加上 const 关键字。
需要强调的是,必须在成员函数的声明和定义处同时加上 const 关键字。
char *getname() const
和char *getname()
是两个不同的函数原型,如果只在一个地方加 const 会导致声明和定义处的函数原型冲突。 -
const位置区分
- 函数开头的 const 用来修饰函数的返回值,表示返回值是 const 类型,也就是不能被修改,例如
const char * getname()
。 - 函数头部的结尾加上 const 表示常成员函数,这种函数只能读取成员变量的值,而不能修改成员变量的值,例如
char * getname() const
。
- 函数开头的 const 用来修饰函数的返回值,表示返回值是 const 类型,也就是不能被修改,例如
-
mutable
C++11中定义了mutable关键字,如果我们定义一个了一个const成员函数,但是在某些场景下,我们还是希望能够修改成员数据,那么我们将数据成员声明为mutable,这样不管const成员函数还是非const成员函数都可以修改该数据成员了。
-
const类对象
非const类对象既可以访问const成员函数也可以访问非const成员函数,const类对象只能访问const成员函数。
const类对象声明格式:const <类型说明符> <对象名> 。
由于const类对象不能对成员数据进行修改,因此在声明的时候,必须对成员数据通过类构造函数进行初始化。