三、其他细节
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 ;
}