类与对象知识总结+封闭类+const+this指针 C++程序设计与算法笔记总结(三) 北京大学 郭炜(下)

简介: 类与对象知识总结+封闭类+const+this指针 C++程序设计与算法笔记总结(三) 北京大学 郭炜(下)

常量成员函数

如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加 const关键字。

常量成员函数(const member function)是指在类中声明的成员函数,在其声明末尾添加const关键字。常量成员函数承诺不会修改对象的状态,因此它们不能修改类的非静态成员变量,也不能调用非常量成员函数(除非这些成员函数也被声明为常量成员函数)。

常量成员函数对于处理只读操作或者保护数据的完整性很有用,因为它们可以确保在使用常量对象或通过常量引用/指针访问对象时,不会意外地修改对象的状态。

下面是一个示例代码:

class MyClass {
private:
    int data;
public:
    int getValue() const {
        // 这是一个常量成员函数,不能修改成员变量
        return data;
    }
    void setValue(int value) {
        // 非常量成员函数可以修改成员变量
        data = value;
    }
};

在上述示例中,getValue()被声明为常量成员函数,因此它不能修改data成员变量的值。而setValue()是非常量成员函数,可以修改data成员变量。

当你有一个常量对象时,只能调用常量成员函数来访问其成员变量和执行操作。例如:

int main() {
    const MyClass obj;
    int value = obj.getValue();  // 可以调用常量成员函数
    // obj.setValue(10);  // 错误,常量对象无法调用非常量成员函数
    return 0;
}

需要注意的是,在常量成员函数中不能修改成员变量,也不能调用非常量成员函数,除非这些非常量成员函数也被声明为常量成员函数。

常量成员函数在类中声明的成员函数末尾添加const关键字。它们承诺不会修改对象的状态,因此对于只读操作或保护数据完整性很有用。常量成员函数可以在常量对象或通过常量引用/指针访问对象时调用,并且不能修改成员变量或调用非常量成员函数(除非这些成员函数也被声明为常量成员函数)。

当使用常量成员函数时,还需要注意以下几点:

  1. 常量对象调用:常量对象只能调用常量成员函数。这是因为常量对象被视为不可修改的,所以只能使用常量成员函数来访问对象的状态。
  2. 重载:常量成员函数和非常量成员函数可以进行重载。如果有两个成员函数,一个是常量成员函数,另一个是同名的非常量成员函数,它们可以根据调用对象的常量性来区分。
class MyClass {
public:
    int getValue() const {
        // 常量成员函数
        return 10;
    }
    int getValue() {
        // 非常量成员函数
        return 20;
    }
};
int main() {
    const MyClass obj1;
    MyClass obj2;
    int value1 = obj1.getValue();  // 调用常量成员函数
    int value2 = obj2.getValue();  // 调用非常量成员函数
    return 0;
}
  1. 返回类型:常量成员函数可以返回实际值,也可以返回常量引用或指针。如果返回一个非常量类型的实际值,它会被复制到调用者的副本中;如果返回常量引用或指针,则避免了无谓的复制。
  2. mutable关键字:在常量成员函数中,如果希望修改某些成员变量,可以使用mutable关键字来修饰这些成员变量。被mutable修饰的成员变量可以在常量成员函数中被修改。
class MyClass {
private:
    mutable int cacheValue;
public:
    int getValue() const {
        if (cacheValue == 0) {
            // 计算并缓存值
            cacheValue = calculateValue();
        }
        return cacheValue;
    }
    int calculateValue() const {
        // 计算值的逻辑
        return 42;
    }
};

在上述示例中,cacheValue成员变量被声明为mutable,因此可以在常量成员函数中更新它的值。

常量成员函数在保护对象状态的同时,也提供了对只读操作的方便访问。通过合理使用常量成员函数,可以增强代码的安全性和可靠性,并遵循面向对象设计的原则。

常量成员函数是指在类中声明的成员函数末尾添加const关键字。它们只能用于常量对象或通过常量引用/指针访问对象,并且不能修改成员变量或调用非常量成员函数(除非这些成员函数也被声明为常量成员函数)。常量成员函数可以进行重载,返回实际值、常量引用或指针,并且可以使用mutable关键字修饰某些成员变量以在常量成员函数中进行修改。

如果一个成员函数中没有调用非常量成员函数,也没有修改成员变量的值,那么,最好将其写成常量成员函数。

对C++中const的说明

在C++中,const是一个关键字,用于指定对象或变量是只读的,即不可修改。它可以应用于不同的上下文中,包括:

  1. 对象和变量声明:通过在变量或对象的声明前加上const关键字,可以将其标记为只读。这意味着一旦被初始化,就不能再修改该对象或变量的值。
const int x = 10; // 声明一个只读的整数常量x
const MyClass obj; // 声明一个只读的MyClass对象
  1. 函数参数:使用const关键字修饰函数参数,表示该参数在函数内部是只读的,在函数执行过程中不能被修改。
void print(const std::string& str) {
    // 该函数不能修改str的内容
    std::cout << str << std::endl;
}
  1. 成员函数:在成员函数后面添加const关键字,表示该成员函数是一个常量成员函数。常量成员函数承诺不会修改对象的状态,并且只能调用其他常量成员函数或访问类的只读成员变量。
class MyClass {
public:
    void foo() const {
        // 这是一个常量成员函数
        // 不能修改成员变量或调用非常量成员函数
    }
};
  1. 返回类型:const关键字也可以用于指定函数或操作符的返回类型是只读的。
const int calculateValue() {
    // 返回一个只读的整数值
    return 42;
}
const MyClass operator+(const MyClass& obj) const {
    // 返回一个只读的MyClass对象
    // 不能修改当前对象或调用非常量成员函数
}

const关键字对于增强代码的可读性、安全性和可维护性非常有帮助。它可以避免意外的修改,保护数据的完整性,并提供了更好的接口设计和封装性。

需要注意的是,使用const关键字并不意味着该对象或变量在内存中是只读的,而仅仅表示在代码中对其进行修改是不被允许的。

当使用const关键字时,还有一些细节和注意事项需要考虑:

  1. 可以重载非constconst成员函数:在同一个类中,可以同时定义一个非const版本和一个const版本的成员函数。这样,在调用对象为常量或非常量时,编译器会根据调用对象的常量性选择相应的成员函数。
class MyClass {
public:
    void foo() {
        // 非const 版本的成员函数
    }
    void foo() const {
        // const 版本的成员函数
    }
};
  1. 常量对象只能调用常量成员函数:常量对象只能调用常量成员函数,因为常量对象被视为只读对象,不允许修改其状态。但非常量对象可以调用常量成员函数和非常量成员函数。
void someFunction(const MyClass& obj) {
    obj.foo();  // 可以调用常量成员函数
    MyClass nonConstObj;
    nonConstObj.foo();  // 也可以调用非常量成员函数
}
  1. 返回类型是const的影响:如果函数返回类型是const,则返回的值通常不能被修改。
const int getValue() {
    return 42;  // 返回的值是只读的
}
int main() {
    const int value = getValue();
    // value = 10;  // 错误,value是只读的
    return 0;
}
  1. 指针和引用的const:当使用指针或引用时,const关键字可以应用于指针本身或指向的对象。这样可以限制对指针或引用的修改,或者限制被指向的对象的修改。
int x = 10;
const int* ptr = &x;  // 指向常量的指针,不能通过ptr修改x的值
int y = 20;
int* const ref = &y;  // 指向整数的常量指针,不能通过ref修改指针的指向
  1. mutable成员变量:mutable关键字可以用于修饰类的成员变量,它表示该成员变量可以在常量成员函数中被修改。
class MyClass {
private:
    mutable int count;
public:
    void increment() const {
        ++count;  // 在常量成员函数中可以修改mutable成员变量
    }
};

需要注意的是,const关键字应根据需要和语义正确地应用。它可以提高代码的可读性、安全性和可维护性,但也需要谨慎使用以避免过度使用。正确使用const关键字可以帮助捕捉编程错误、保护数据完整性,并提供更好的接口设计和封装性。

当使用const关键字时,还有一些概念和技巧需要了解:

  1. 保证线程安全性:在多线程环境中,常量对象的成员函数是线程安全的。由于常量对象的状态不会被修改,多个线程可以同时访问常量对象的成员函数而无需额外的同步机制。
  2. 常量性转换:常量性可以通过类型转换来进行转换。即可以将非常量对象转换为常量对象进行只读操作。这通过将对象引用或指针的类型从非常量改变为常量来实现。
void func(const MyClass& obj) {
    // 可以接受常量对象作为参数并进行只读操作
}
int main() {
    MyClass obj;
    const MyClass& constRef = obj;  // 将非常量对象转换为常量引用
    const MyClass* constPtr = &obj;  // 将非常量对象的地址转换为常量指针
    return 0;
}
  1. const和函数重载:常量性可以用作函数重载的条件之一。如果一个函数的参数是常量对象或常量引用,那么可以重载该函数以提供对常量对象的特殊处理。
class MyClass {
public:
    void process() {
        // 非const 版本的成员函数
    }
    void process() const {
        // const 版本的成员函数
    }
};
int main() {
    MyClass obj;
    const MyClass constObj;
    obj.process();      // 调用非const版本的process函数
    constObj.process(); // 调用const版本的process函数
    return 0;
}
  1. const修饰符位置:在函数声明中,const关键字可以放在成员函数的后面,也可以放在参数列表的后面。这两种形式的意义是相同的,但通常将const关键字放在函数后面更为常见。
class MyClass {
public:
    void process() const;   // const放在函数后面
    void update() const;    // const放在参数列表后面
};
void MyClass::process() const {
    // const成员函数的实现
}
void MyClass::update() const {
    // const成员函数的实现
}

需要根据具体情况正确使用const关键字。合理使用const可以增强代码的安全性、可读性和可维护性,并帮助捕捉编程错误。它提供了一种约束机制,用于指定只读操作和不会修改对象状态的函数,从而增加了代码的健壮性和可靠性。

常量成员函数的重载

在C++中,常量成员函数可以根据被调用对象的常量性进行重载。这意味着可以定义一个非常量版本和一个常量版本的成员函数,分别用于处理常量对象和非常量对象。

下面是一个示例代码:

class MyClass {
public:
    void foo() {
        // 处理非常量对象的逻辑
        std::cout << "Non-const version" << std::endl;
    }
    void foo() const {
        // 处理常量对象的逻辑
        std::cout << "Const version" << std::endl;
    }
};
int main() {
    MyClass obj1;
    const MyClass obj2;
    obj1.foo();  // 调用非常量版本的foo函数
    obj2.foo();  // 调用常量版本的foo函数
    return 0;
}

在上述示例中,MyClass类定义了两个名为foo()的成员函数,一个是非常量版本,另一个是常量版本。当调用非常量对象obj1foo()函数时,会调用非常量版本;而当调用常量对象obj2foo()函数时,会调用常量版本。

通过使用常量成员函数的重载,可以根据对象的常量性来选择合适的操作方式。这样可以保证对常量对象的只读操作以及非常量对象的修改操作都能得到正确的处理。

需要注意的是,常量成员函数的重载不仅与常量性有关,还与函数的参数类型和数量相关。因此,在进行函数重载时,需要确保函数的签名(包括参数类型、常量性等)是不同的。

总结一下,常量成员函数可以根据对象的常量性进行重载,以提供对常量对象和非常量对象的不同处理。通过合理使用常量成员函数的重载,可以保证对象在不同常量性下得到适当的操作,并增加代码的灵活性和可读性。

mutable成员变量(可以在const成员函数中修改的成员变量)

可以在const成员函数中修改的成员变量

在C++中,mutable关键字用于修饰类的成员变量,它表示该成员变量可以在常量成员函数中被修改,即使这些函数通常是不允许修改对象状态的。

下面是一个示例代码:

class MyClass {
private:
    mutable int count;
public:
    void increment() const {
        ++count;  // 在常量成员函数中可以修改mutable成员变量
    }
};

在上述示例中,count成员变量被声明为mutable,这意味着即使在常量成员函数(如increment())中,也可以对其进行修改。默认情况下,常量成员函数是不允许修改对象的状态的,但使用mutable关键字可以打破这个限制。

mutable关键字适用于那些在实现细节中需要跟踪或缓存信息的成员变量。例如,在某个类中计算并缓存某个值,而不希望每次调用时都重新计算,可以使用mutable来标记该成员变量。

需要注意以下几点:

  • mutable只能应用于非静态成员变量,因为静态成员变量是与类而不是对象相关联的。
  • mutable成员变量的修改仅限于同一对象内部,并不会影响其他对象的状态。
  • 尽管mutable允许在常量成员函数中修改变量,但仍应该谨慎使用。这是因为常量成员函数通常被认为是不会引起对象状态变化的,因此对于需要修改的情况,最好考虑其他可行的设计方案。

总结一下,mutable关键字用于修饰类的成员变量,表示该成员变量可以在常量成员函数中被修改。它在某些情况下提供了更灵活的设计选择,但也应该谨慎使用,以避免滥用导致代码逻辑混乱或违背设计原则。

目录
相关文章
|
12天前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
46 12
|
1月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
1月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
2月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
1月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
53 16
|
1月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
2月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
1月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
124 6
|
3月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
117 19
|
2月前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。

热门文章

最新文章

下一篇
oss创建bucket