三、其他细节
1. this 指针
1. 引入
在早前的编码中,我们都会刻意的避免的函数的参数名称和成员变量名称一致,这是为了让我们更方便的赋值。其实他们的名称也可以一样,只是要多费点功夫而已。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #include <iostream>
#include <string>
class Student{
int age ;
string name;
Student(int age_val , string name_val){
//默认情况下,前面的两个变量有编译器加上的this指针
age = age_val ;
name = name_val ;
}
Student(int age , string name){
//默认情况下,下面两行代码并没有完成赋值工作。
age = age ;
name = name ;
//=============================
//前面加上this -> 即可完成操作。
this-> age = age ;
this-> name = name ;
}
};
|
2. 含义
this
只能在成员函数中使用。全局函数,静态函数都不能使用this
。全局函数不属于任何一个类,静态函数也不依赖任何一个类,所以他们都不会有this指针
。 在外部看来,每一个对象都拥有自己的成员函数,一般情况下不写this,而是让系统进行默认配置。this指针永远指向当前对象 , 所以成员函数中的,通过this即可知道操作的是哪个对象的数据。
this指针的作用有两个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | #include <iostream>
#include <string>
class Student{
int age ;
string name;
Student(int age , string name){
//默认情况下,下面两行代码并没有完成赋值工作。
age = age ;
name = name ;
//=============================
//前面加上this -> 即可完成操作。
this-> age = age ;
this-> name = name ;
}
//获取对象本身。注意: 这里的返回值不要写成Student,否则返回的是一个临时的拷贝对象
Student& getStu(){
return *this; //this表示指针, 在指针前面加上* 表示解引用。根据指针获取到指向的对象
}
};
int main(){
Student s1 (18 , "张三");
Student &s2 = s1.getStu(); //使用引用来接收,表示指向同一个引用,
return 0 ;
}
|
2. 常函数
在编写代码的时候,如果确定了某个成员函数不会修改成员变量,那么可以把该函数声明为常函数。常函数其实也是函数,它浓缩了函数和常
这个概念在里面,函数体内不允许修改成员变量,除非该变量使用mutable
修饰.
常函数的const修饰的是this指针,在常函数中的this形同:const 类名
, 表示指向常量的指针。 所以也就解释了为什么在常函数中无法修改成员变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #include<iostream>
#include<string>
using namespace std;
class Student{
public:
string name = "无名氏";
int age = 18;
public :
//在此添加 const 即可变成常函数,
void printStudent() const{
// age = 10 ; 错误,不允许修改
cout << name << " " << age << endl;
}
};
|
3. 常对象
常对象其实也就是一个变量,和以前的常量没有相差多少。不同的是如今对象里面可以能包含很多的变量,以前的常量指的只是一个变量而已。 若类中有变量使用mutable
修饰,那么该变量在常对象状态下,可以被修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | #include<iostream>
#include<string>
using namespace std;
class Student{
public:
string name = "无名氏";
int age = 18;
public :
//在此添加 const 即可变成常函数,
void printStudent() const{
cout << name << " " << age << endl;
}
void run(){
cout<< age << "的" <<name <<"在跑步" <<endl;
}
};
int main(){
const Student s1 ("zhangsan " ,18);
cout << s1.name << endl; //访问常对象中的成员 ,OK
s1.name = "李四" ; // 试图修改常对象中的成员 , error
return 0 ;
}
|
- 注意:
-
- 无法修改常对象中的成员(变量)
- 除非某个成员使用
mutable
修饰
- 常函数函不能访问普通的成员函数,但是可以访问常函数
- 普通的对象可以访问成原函数,也可以访问常函数
4. 静态成员
有时候需要不管创建多少次对象,某些变量的值都是一样的,那么此时可以把该变量变成静态修饰(static).
静态成员被类的所有对象所共享,成员不能在类中定义的时候初始化,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化
1. 静态成员变量
假设现在要记录来参加当年毕业的学生信息,无论他们的身份迥异,都有一个共有的属性,就是同一个学校。如果不对该属性进行修饰,那么100个学生对象就要开辟100个位置来存储同一个school数据,但是其实可以使用static修饰,只需要一份空间即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | #include<iostream>
#include<string>
using namespace std;
class Student{
public:
int age ;
string name;
static string school; //静态成员
};
Student::school = "北京大学";
int main(){
Student s;
s.name = "张三";
s.age = 18 ;
cout <<s.school << " " << s.name <<" " << s.age << endl;
return 0 ;
}
|
2. 静态成员函数
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | #include<iostream>
#include<string>
using namespace std;
class Student{
public:
int age ;
string name;
static string school; //静态成员
public :
static string getSchool(){
//不能访问 name 和 age
return school;
}
};
Student::school = "北京大学";
int main(){
Student s ;
cout << s.getSchool() << endl; //可以访问
//也可以使用类名的方式访问
cout << Student::school <<endl;
return 0 ;
}
|
- 注意:
- 静态成员属于类,不属于对象
- 静态成员变量必须在类中声明,在类的外部初始化
- 静态成员变量的声明周期是整个程序,作用域在类的内部
- 静态成员函数只能访问静态成员变量
- 静态成员可以使用类来访问,也可以使用对象来访问。
5. 结构体和类
结构体是一个由程序员定义的数据类型,可以容纳许多不同的数据值。在过去,面向对象编程的应用尚未普及之前,程序员通常使用这些从逻辑上连接在一起的数据组合到一个单元中。一旦结构体类型被声明并且其数据成员被标识,即可创建该类型的多个变量,就像可以为同一个类创建多个对象一样。 结构体是由C语言发明出来的,它和类其实没有差多少,只是它的所有成员默认都是public公开。
声明结构体的方式和声明类的方式大致相同,其区别如下:
- 使用关键字 struct 而不是关键字 class。
- 尽管结构体可以包含成员函数,但它们很少这样做。所以,通常情况下结构体声明只会声明成员变量。
- 结构体声明通常不包括 public 或 private 的访问修饰符, 因为它的所有成员默认都是public
- 类成员默认情况是私有的,而结构体的成员则默认为 public。程序员通常希望它们保持公开,只需使用默认值即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | #include<iostream>
#include<string>
struct Student{
string name;
int age;
void run(){
}
}
int main(){
Student s1 ;
s1.name = "zhangsan";
s1.age = 18 ;
return 0 ;
}
|
6. 友元
私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。
在类的外部无法访问类的私有成员,但是有时候需要它,那么可以借助友元函数 来实现。友元函数
是一种特权函数,C++允许它访问私有成员。程序员可以把一个全局函数、成员函数、甚至整个类声明为友元
友元可以看成是现实生活中的 好闺蜜 或者是 好基友
1. 友元函数
友元函数可以直接访问类中的私有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #include<iostream>
#include<string>
using namespace std;
class Car{
private:
string color {"红色"};
friend void showColor(Car c);
};
//实现友元函数,函数内部可以直接访问私有成员
void showColor(Car c) {
cout << c.color << endl;
}
int main(){
Car c ;
showColor(c);
return 0 ;
}
|
2. 友元类
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | #include<iostream>
#include<string>
using namespace std;
class Car{
private:
string color {"红色"};
friend class SSSS; //声明 4S 为友元类
public:
string getColor(){
return color;
}
};
class SSSS{
public:
void modifyCar( Car &myCar){ //改装汽车
myCar.color = "黑色";
}
};
int main(){
SSSS s;
Car c;
s.modifyCar(c);
cout << c.getColor() << endl;
return 0 ;
}
|