this,常成员函数,常对象

介绍

this关键字

在C++中,this是一个指针,指向当前对象的实例。它在类的成员函数中使用,允许你访问对象的成员变量和成员函数。以下是关于this指针的一些关键点:

(1) 指向当前对象:this指针是一个隐式参数,指向调用成员函数的对象。例如,在一个成员函数中,this->memberVariable可以用来访问当前对象的成员变量。

(2) 类型:this的类型是指向类的指针。例如,在类MyClass的成员函数中,this的类型是MyClass*。

(3) 用于区分成员变量和参数:当成员变量的名称与参数名称相同时,可以使用this来区分。例如:

class MyClass {  
public:  
    int value;  
    MyClass(int value) {  
        this->value = value; // 使用this指针区分成员变量和参数  
    }  
};

(4) 返回当前对象:this指针可以用于返回当前对象的引用,常用于链式调用。例如:

class MyClass {  
public:  
    MyClass& setValue(int value) {  
        this->value = value;  
        return *this; // 返回当前对象的引用  
    }  
};

(5) 在静态成员函数中不可用:this指针只能在非静态成员函数中使用,因为静态成员函数不属于任何特定对象。

(6) 常量成员函数中的this:在常量成员函数中,this的类型是指向常量对象的指针(const ClassName*),这意味着你不能在常量成员函数中修改对象的成员变量。

以下是一个简单的示例,展示了this的用法:

#include <iostream>  
using namespace std;  

class MyClass {  
private:  
    int value;  

public:  
    MyClass(int value) {  
        this->value = value; // 使用this指针  
    }  

    MyClass& setValue(int value) {  
        this->value = value; // 使用this指针  
        return *this; // 返回当前对象的引用  
    }  

    void display() const {  
        cout << "Value: " << this->value << endl; // 使用this指针  
    }  
};  

int main() {  
    MyClass obj(10);  
    obj.display(); // 输出: Value: 10  
    obj.setValue(20).display(); // 链式调用,输出: Value: 20  
    return 0;  
}

常成员函数和常对象

在C++中,常成员函数和常对象是用于控制对象状态和行为的重要概念。它们通过使用const关键字来确保对象的不可变性,从而提高代码的安全性和可读性。下面是对这两个概念的详细讲解:

常对象(const Objects)

常对象是指在创建对象时,使用const关键字修饰的对象。这意味着该对象的状态(即其成员变量)不能被修改。

特点:

不可修改:常对象的非静态成员变量不能被修改,尝试修改会导致编译错误。

只能调用常成员函数

常量引用:常对象通常通过常引用传递,以确保在函数调用中不会修改对象。

class MyClass {  
public:  
    int value;  

    MyClass(int v) : value(v) {}  
};  

void display(const MyClass& obj) {  
    // obj.value = 10; // 错误:不能修改常对象的成员  
    std::cout << "Value: " << obj.value << std::endl;  
}  

int main() {  
    const MyClass obj(10); // 创建常对象  
    display(obj); // 正确,传递常对象  
    return 0;  
}

在这个示例中,obj是一个常对象,display函数接受一个常对象的引用作为参数,确保在函数内部不会修改obj的状态。

常成员函数(const Member Functions)

常成员函数是指在成员函数的声明中使用const关键字修饰的函数。这表示该函数不会修改调用该函数的对象的状态。

不能修改成员变量:在常成员函数中,不能修改任何非静态成员变量。

可以被常对象和非常对象调用

class MyClass {  
private:  
    int value;  

public:  
    MyClass(int v) : value(v) {}  

    // 常成员函数  
    void display() const {  
        std::cout << "Value: " << value << std::endl; // 可以读取,但不能修改  
    }  

    // 非常成员函数  
    void setValue(int v) {  
        value = v; // 这是一个非常成员函数,可以修改  
    }  
};  

int main() {  
    MyClass obj(10);  
    obj.display(); // 输出: Value: 10  

    const MyClass constObj(20);  
    constObj.display(); // 输出: Value: 20  
    // constObj.setValue(30); // 错误:不能调用非常成员函数  
    return 0;  
}

在这个示例中,display是一个常成员函数,它可以在常对象上调用,而setValue是一个非常成员函数,不能在常对象上调用。

常成员函数与常对象的关系

常对象只能调用常成员函数:如果你有一个常对象,你只能调用该对象的常成员函数。这是为了确保对象的状态不会被意外修改。

常成员函数可以被常对象和非常对象调用:常成员函数可以被常对象和非常对象调用,但在常对象上调用时,函数内部不能修改对象的状态。

使用场景

常对象:常对象通常用于需要保护对象状态不被修改的场景,例如在函数参数中传递对象时,确保不会意外修改对象。

常成员函数:常成员函数用于提供只读访问对象状态的接口,确保对象的状态在调用过程中保持不变。

用法

常成员函数和常对象很多人并不在意,确实都写普通变量也可以;但是在大型程序中,尽量加上const 关键字可以减少很多不必要的错误。

(1) 常成员函数和常对象

常成员函数就是无法修改成员变量的函数。可以理解为将this指针指向对象用const修饰的函数;

常对象就是用 const 修饰的对象,定义好之后就再也不需要更改成员变量的值了。 常对象在大型程序中还是很有意义的;

#include <iostream>

class Test
{
public:
    Test(std::string name_, int age_);
    ~Test();
    void output();         // 普通成员函数
    void display() const;  // 常成员函数,无法修改成员变量;
    // 常成员函数 本质是普通成员函数的this加上const修饰,但是参数中没有this,所以直接函数后加个const
    
    std::string name;
    int age;
};

Test::Test(std::string name_, int age_) : name(name_), age(age_) {}
Test::~Test(){}

// 普通成员函数实现
void Test::output()
{
    std::cout << "调用普通成员函数" << std::endl;
    std::cout << "name = " << name << ", age = " << age << std::endl;
}

// 普通成员函数的本质 全局函数 + this指针(Test类指针常量)
void output(Test* const myThis) 
{
    std::cout << "调用普通成员函数" << std::endl;
    std::cout << "name = " << myThis->name << ", age = " << myThis->age << std::endl;
}

// 常成员函数实现
void Test::display() const
{
    std::cout << "调用常成员函数" << std::endl;
    //myThis->age = 100; // 此时不能修改指针指向的对象
}


// 常成员函数的本质 全局函数 + this指针(Test类指针常量指向常量)
void display(const Test* const myThis)
{
    std::cout << "调用常成员函数" << std::endl;
    //myThis->age = 100; // 此时不能修改指针指向的对象
}


int main() 
{
    Test t1("zhangsan", 20);

    t1.output();
    output(&t1);      // 传入对象地址

    t1.display();
    display(&t1);      // 传入对象地址

    const Test t2("lisi", 25);   // 不希望t2对象被修改,所以使用常对象
    //t2.age = 80;                 // t2是常对象,不能修改对象t2

    return 0;
}

(2) 常成员函数注意事项:

因为类的成员函数已经将 this 指针省略了,只能在函数后面加 const 关键字来实现 无法修改类成员变量的功能了(上述代码里也进行了演示)

  1. 常函数无法调用了普通函数,否则常函数的这个“常”字还有什么意义

解释:如果一个常函数里可以调用普通函数,那么我们可以调用set函数,去修改对象,那么此时这个常成员函数就没有意义了,所以语法规定,常函数无法调用普通函数

  1. 成员函数能写作常成员函数就尽量写作常成员函数,可以减少出错几率
  2. 同名的常成员函数和普通成员函数是可以重载的,常量对象会优先调用常成员函数,普通对象会优先调用普通成员函数
#include <iostream>

class Test
{
public:
    Test(std::string name_, int age_);
    ~Test();
    // 两个output函数是重载关系
    void output();         // 普通成员函数
    void output() const;   // 常成员函数
    
    std::string name;
    int age;
};

Test::Test(std::string name_, int age_) : name(name_), age(age_) {}
Test::~Test(){}

void Test::output()
{
    std::cout << "调用普通成员函数output" << std::endl;
    std::cout << "name = " << name << ", age = " << age << std::endl;
}

void Test::output() const
{
    std::cout << "调用常成员函数output" << std::endl;
    std::cout << "name = " << name << ", age = " << age << std::endl;
}


int main() 
{
    Test t1("zhangsan", 20);  //  普通对象
    t1.output();              //  普通对象会优先调用普通成员函数

    const Test t2("lisi", 30); // 常对象
    t2.output();               // 常对象会优先调用常成员

    return 0;
}

常对象注意事项:

  • 常对象不能调用普通函数,原因和常成员函数不能调用普通函数是一样的
  • 常成员函数和常对象要多用,这真的 是一个非常好的习惯,写大项目可以少出很多bug