三、其他细节#

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 ;
}
  • 注意:
    1. 无法修改常对象中的成员(变量)
    2. 除非某个成员使用mutable修饰
    3. 常函数函不能访问普通的成员函数,但是可以访问常函数
    4. 普通的对象可以访问成原函数,也可以访问常函数

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 ;
}