一、类的成员函数与内联
在类内定义的所有函数都自动称为内联函数。如果在类内声明,在类外定义,也可以定义为内联函数。在定义函数时添加inline限定符。。
(1)此外,内联函数一定要和函数定义在一起,否则只是声明的时候加上内联,是不会成为内联函数的
声明:inline void fun();
函数体:void fun(){}
这样只有声明带内联,函数定义时没有带内敛,所以fun不是内联函数。
(2)inline一定要和函数定义在一起。
声明:void fun();
定义:inline void fun(){}
这样可以,但是后来我才知道这样还会存在一点问题。比如下面。
(3)
声明:void fun();
调用:fun();
定义:inline fun(){}
声明没有inline,定义有inline,但是调用在定义前,这样也不会成为内联。(这一点是我在一篇2015年博客上看到后才知道的,但是现在这样到底会不会成为内联函数,我也不确定)
但是最保险的做法是,定义和声明都加上inline关键字。
除了这些之外,编译器会自动取消掉不合适的内联。
二、普通成员函数的this指针
类的成员变量和成员函数是分开存储的,只有非静态成员变量才属于实例化出来的对象,每个对象都有一份。非静态成员函数只有一份。
类的每一个非静态成员函数都有一个this指针。而且是指针常量。即T* const this类型。T为类型。所以可以看出不能更改this指针的指向。而且是这个形参是隐式包含在形参列表中的。this指针指向调用这个成员函数的对象本身。
用途:
(1)区分与成员变量相同名称的形参。
即如果成员函数的形参与成员变量名相同,那么可以用this->“成员变量名”,以这种形式区分成员变量和形参。
(2)返回对象本身。
this是指向对象本身的,那么在成员函数体内,*this就是指对象本身。
三、空指针访问成员函数
在C++中空指针是可以调用成员函数的。如果用到this指针了,那么就会报错,如果没有用到this指针,就不会报错。这个时候就要考虑代码的健壮性了。比如下面举例:
代码1:
class Student
{
public:
Student()
{
cout << "this is Student()" << endl;
}
~Student()
{
cout << "this is ~Student()" << endl;
}
void fun1()
{
cout << "this is fun()" << endl;
}
void fun2()
{
this->id = 10;
}
private:
int id;
string name;
};
void test()
{
Student* p = nullptr;
p->fun1();
}
int main()
{
test();
return 0;
}
运行结果:
代码2:
class Student
{
public:
Student()
{
cout << "this is Student()" << endl;
}
~Student()
{
cout << "this is ~Student()" << endl;
}
void fun1()
{
cout << "this is fun1()" << endl;
}
void fun2()
{
this->id = 10;
cout << "this is fun2()" << endl;
}
private:
int id;
string name;
};
void test()
{
Student* p = nullptr;
p->fun2();
}
int main()
{
test();
return 0;
}
运行结果:
用的编译器是vs2019,出乎意料的是居然没有崩溃,但是程序返回值是一个很多位的随机值,说明返回的是错误码,之前都是直接弹出来窗口说崩溃。按道理来说是理应崩溃的。因为我们在fun2()里面用了this->这个语句。->有解引用步骤,对空指针解引用,肯定不行,所以理应崩溃,可能是VS2019编译器优化了返回错误码,没有弹窗告诉崩溃。运行没崩溃,但是我调试了一下,报错了。
所以一般情况下,为了防止这种情况发生,最好在成员函数里,对this指针加一个非空判断。
四、const修饰的成员函数和成员变量
1.基本概念
(1)成员函数后加上const后,我们称之为常函数,常函数不能修改成员属性。
(2)成员变量加上const后,称之为常变量。不能修改值。常成员变量一定要初始化。
(3)定义对象前加const,即为长对象,由于常对象的属性不允许修改,所以常对象只能调用常函数。比如const Student stu;stu就是常对象。一下举例常对象和常成员函数
代码:
class Student
{
public:
Student(int i, string na) :id(i), name(na)
{
cout << "this is Student()" << endl;
}
~Student()
{
cout << "this is ~Student()" << endl;
}
int GetId()const
{
cout << "this is GetId()const" << endl;
return id;
}
int GetId()
{
cout << "this is GetId()" << endl;
return id; return id;
}
private:
int id;
string name;
};
int main()
{
const Student stu1(100,"xiaomeng");
stu1.GetId();
Student stu2(200, "xiaohua");
stu2.GetId();
return 0;
}
运行结果:
注意,我们对GetId函数重载了。stu1是常对象,在这里只能调用常成员函数GetId()const。stu2是普通对象,可以调用GetId(),也可以调用GetId()const,而且还都是完全匹配。只不过GetId()更有优先性。这里对象调用重载的成员函数规则,和我们函数重载规则一样。
其中如果我们想要在常成员函数里修改某一个成员变量,而其他成员变量仍要求不能修改的话,我们可以用mutable关键字,如下:
class Student
{
public:
Student(int i, string na) :id(i), name(na)
{
cout << "this is Student()" << endl;
}
~Student()
{
cout << "this is ~Student()" << endl;
}
int GetId()const
{
name = "xiaomeng";
//id = 200;//error
cout << "this is GetId()const" << endl;
return id;
}
void Show()
{
cout << "name: " << name << " " << "id: " << id << endl;
}
private:
int id;
mutable string name;
};
int main()
{
Student stu(100, "xiaohua");
stu.GetId();
stu.Show();
return 0;
}
注意,成员变量name加了关键字mutable,GetId()const函数里,对name修改了。
运行结果:
发现name从“xiaohua”修改成了“xiaomeng”。
五、静态成员变量
(1)所有对象共用一个静态成员变量。
(2)类内声明,必须在类外初始化,在类内不允许给初始值。(const static类型必须在类内给初始值)
(3)在编译阶段就给静态变量分配内存,存储在静态变量区。受访问权限限制。
(4)静态成员变量只有被初始化后才能调用,否则调用静态变量编译出错。
(5)当静态成员变量访问权限为公有时,可以用对象名访问,可以用成员函数访问,可以用类名访问。当静态成员变量为私有成员权限时,只可以以调用成员函数的方式(包括普通成员函数和静态成员函数)访问。
class Student
{
public:
Student(int i, string na) :id(i), name(na) {}
~Student() {}
static void fun1()
{
//可以访问静态成员变量
cout << "this is static fun1()" << "s_1 = " << s_t1 << " " << "s_t2 = " << s_t2 << endl;
}
void Show()
{
cout << &(this->s_t1) << endl;
}
private:
const static int s_t1 = 10;//静态常量成员可以直接在类内初始化
static int s_t2;//非常量静态成员变量必须在类外初始化。
int id;
string name;
};
int Student::s_t2 = 50;
int main()
{
Student stu1(100, "xiaohua");
stu1.fun1();
stu1.Show();
Student stu2(200, "xiaomeng");
stu2.fun1();
stu2.Show();
return 0;
}
运行结果:
六、静态成员函数
(1)静态成员函数没有this指针
(2)所有对象共享一个静态成员函数。
(3)静态成员函数只能访问静态成员变量(没有this指针)。
(4)静态成员函数类内类外都可以定义。
(5)静态成员函数受访问权限的限制
访问静态成员变量的方式:
(1)通过对象访问
(2)通过域名访问
如下举例静态成员函数的声明,在类内定义,类外定义,和调用方式:
class Student
{
public:
Student(int i, string na) :id(i), name(na)
{
cout << "this is Student()" << endl;
}
~Student()
{
cout << "this is ~Student()" << endl;
}
static void fun1()
{
//id = 200;//error,静态成员函数没有this指针,没办法访问成员变量.如果写成这种static void fun(Student stu),可以访问形参的成员变量
cout << "this is static void fun1()" << endl;
}
static void fun2();
void Show()
{
cout << "name: " << name << " " << "id: " << id << endl;
}
private:
static void fun3()
{
cout << "this is static fun3()" << endl;
}
int id;
string name;
};
void Student::fun2()//类外定义静态成员函数
{
cout << "this is static fun2()" << endl;
}
int main()
{
Student stu(100, "xiaohua");
stu.fun1();//通过对象访问静态成员函数
Student::fun1();//通过域名访问静态成员函数
return 0;
}
注意fun3是私有权限,不可以在类外用对象或者类名访问。