前言
上篇中介绍了一些成员函数、构造函数、析构函数等,本篇将介绍成员初始化列表、new delete、数组、static静态、this指针等。
成员初始化列表
前面介绍的构造函数都采用了常见的函数方式对数据成员进行初始化,即在函数体重通过赋值语句对数据成员赋初值。事实上,构造函数还可以采用成员初始化列表的方式对数据成员进行初始化。在某些情况下,还必须采用初始化列表的方式才能完成成员的初始化。成员初始化列表的形式:
构造函数名(参数表): 成员1(初始值参数1),成员2(初始值参数2),... {
...
}
举个例子:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Teacher {
private:
char* name;
public:
Teacher(char* name) {
this->name = name;
cout << "Teacher有参构造函数" << endl;
}
~Teacher() {
cout << "Teacher析构函数" << endl;
}
char* getName() {
return this->name;
}
};
class Student {
private:
int id;
//属性对象
Teacher t1;
public:
Student(int id, char* t1_n): t1(t1_n){
this->id = id;
cout << "Student有参构造函数" << endl;
}
~Student() {
cout << "Student析构函数" << endl;
}
void myPrintf() {
cout << t1.getName() << "," << id << endl;
}
};
void func() {
Student s1(20, "john");
s1.myPrintf();
}
void main() {
func();
getchar();
}
此例中要想在Student
的构造函数中做到对类Teacher
的name
属性进行初始化并且保证每个Teacher
的name
可以不一样(如果一样可以通过Teacher t1 = Teacher("john")
),就必须对其属性对象采用如此方式进行初始化。
运行打印结果为:
Teacher有参构造函数
Student有参构造函数
john,20
Student析构函数
Teacher析构函数
从打印结果分析得出:先调用属性对象所属类的构造函数,再调用属性对象所在类的构造函数,释放的时候,首先调用属性对象所在类的析构函数,再调用属性对象所属类的构造函数。
注:
对象所属类,比如Teacher t1,t1这个对象就是属于Teacher这个类,但是由于t1在Student这个类中,所以t1这个对象所在类为Student.
new delete
C++沿用了C的动态内存分配方式malloc
(free
),同时还新增了new
(delete
)动态内存分配方式。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Teacher {
private:
char* name;
public:
Teacher(char* name) {
this->name = name;
cout << "有参构造函数" << endl;
}
~Teacher() {
cout << "析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
char* getName() {
return this->name;
}
};
void func() {
//C++
Teacher *t1 = new Teacher("john");
cout << t1->getName() << endl;
//释放
delete t1;
//C
Teacher *t2 = (Teacher*)malloc(sizeof(Teacher));
t2->setName("jack");
cout << t2->getName() << endl;
free(t2);
}
void main() {
func();
getchar();
}
运行可知,通过new
(delete
)方式会调用构造函数和析构函数,而通过malloc
(free
)方式不会调用构造函数和析构函数。
数组
void main() {
func();
//数组类型
//C
int *p1 = (int*)malloc(sizeof(int)* 10);
p1[0] = 9;
free(p1);
//C++
int* p2 = new int[10];
p2[0] = 10;
//释放数组[]
delete[] p2;
getchar();
}
static静态属性和方法
static静态属性
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Teacher {
public:
char* name;
//计数器
static int total;
public:
Teacher(char* name) {
this->name = name;
cout << "有参构造函数" << endl;
}
~Teacher() {
cout << "析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
char* getName() {
return this->name;
}
static void count() {
total++;
cout << total << endl;
}
};
//静态属性初始化赋值
int Teacher::total = 10;
void main() {
cout << Teacher::total << endl;
//直接通过类名访问
Teacher::count();
//也可以通过对象名访问
Teacher t("john");
t.count();
getchar();
}
using namespace std;
class Teacher {
public:
char* name;
//计数器
static int total;
public:
Teacher(char* name) {
this->name = name;
cout << "有参构造函数" << endl;
}
~Teacher() {
cout << "析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
char* getName() {
return this->name;
}
};
//静态属性初始化赋值
int Teacher::total = 10;
void main() {
cout << Teacher::total << endl;
getchar();
}
static静态方法
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Teacher {
public:
char* name;
//计数器
static int total;
public:
Teacher(char* name) {
this->name = name;
cout << "有参构造函数" << endl;
}
~Teacher() {
cout << "析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
char* getName() {
return this->name;
}
static void count() {
total++;
cout << total << endl;
}
};
//静态属性初始化赋值
int Teacher::total = 10;
void main() {
cout << Teacher::total << endl;
//直接通过类名访问
Teacher::count();
//也可以通过对象名访问
Teacher t("john");
t.count();
getchar();
}
静态方法可以直接通过类名访问,当然也可以通过对象名访问,但是一般还是用类名的访问方式。
this指针
类的每个对象都有自己的数据成员,有多少个对象,就有多少份数据成员的备份。然而,类的成员函数只有一份备份,不论有多少个对象,都共用这份成员函数,那么不同对象是怎样共用这份成员函数的呢?换句话说,在程序运行过程中,成员函数怎样知道哪个对象在调用它,它应该处理哪个对象的数据成员呢?答案就是this指针。
this是用于标识一个对象自引用的隐式指针,代表对象自身的地址,代表对象自身的地址。
接下来我们来验证一下this是否代表对象自身的地址:
using namespace std;
class Teacher {
private:
char* name;
int age;
public:
Teacher(char* name, int age) {
this->name = name;
this->age = age;
cout << "有参构造函数" << endl;
}
~Teacher() {
cout << "析构函数" << endl;
}
void myPrintf() {
printf("%#x\n", this);
cout << this->name << "," << this->age << endl;
}
};
void main() {
Teacher t1("john", 23);
Teacher t2("jack", 20);
printf("%#x\n", &t1);
t1.myPrintf();
printf("%#x\n", &t2);
t2.myPrintf();
getchar();
}
运行打印结果为:
有参构造函数
有参构造函数
0xd9fdb8
0xd9fdb8
john,23
0xd9fda8
0xd9fda8
jack,20
从打印结果可知,this指针的确是代表对象自身的地址。
在编译类成员函数时,C++编译器会自动将this指针添加到成员函数的参数列表中。在调用类的成员函数时,调用对象会把自己的地址通过this指针传递给成员函数。例如p1.move(10, 20)
会被编译器转换成类似下面的调用形式:
move(&p1, 10, 20);
由于this指针是在程序员不知晓的情况下,由编译器添加到成员函数参数列表中的隐含参数,所以称它为隐式指针。
看到这些是不是觉得特别像Java中的this关键字呢,其实大致是一样的,只不过在C++中this代表的是对象自身的地址,而在Java中表示的是当前对象,其实准确来说也是当前对象的地址,在Java中只是想抹除掉地址的概念,而用当前对象去表示这个概念,而其实要想操作对象的一些信息,还是需要从对象所在的地址去处理。这样一来,是不是对Java中的当前对象理解上升了一个层次呢!
常函数
依据上个例子,
const Teacher t2("jack", 20);
t2.myPrintf(); //错误
将myPrintf
改成常函数
void myPrintf() const {
printf("%#x\n", this);
cout << this->name << "," << this->age << endl;
}
此时t2.myPrintf()
没有报错了,可以得出结论:
常量对象只能调用常函数,不能调用非~常函数(~表示停顿),即非常量函数。
其实此时常函数myPrintf()
中的this
相当于const Teacher* const this
,既不能改变指针的值,又不能改变指针指向的内容。