首页 > 编程语言 >【C++基础】static、const在类中的应用

【C++基础】static、const在类中的应用

时间:2024-08-23 19:56:55浏览次数:11  
标签:友元 const 函数 int 成员 C++ static

目录

static

一、修饰的变量或函数类型

1. 修饰全局变量

2. 修饰局部变量

1. 通过函数访问

2. 通过类的静态成员

3. 修饰函数

4. 修饰类中的成员

二、在类中的应用场景

1. 共享数据(跨对象共享状态)

2. 单例模式

3. 工具类或辅助函数

4. 类级别的常量

5. 计数器或标识符生成器

6. 缓存共享数据

const

一、修饰的变量和函数类型

1. const修饰的变量

2. const修饰的成员变量

3. const修饰的成员函数

3. const修饰的返回值

4. const修饰的常量表达式

5. const修饰的友元函数

友元函数中的const参数:

二、相关内容补充

友元函数的介绍

友元函数的特点和用法

友元类

const和constexpr的区别

1. 定义和目的

2. 编译时与运行时

3. 函数使用

4. 使用场景

5. 构造函数


如需咨询请添加个人微信:a15135158368

欢迎叨扰,多多交流

static

一、修饰的变量或函数类型

1. 修饰全局变量

static 修饰全局变量,限制作用为当前源文件

也就是说,其他源文件不能访问这个变量。这通常用于实现文件内部的数据封装。

// file1.cpp
static int globalVar = 42; // 只能在 file1.cpp 中访问
​
void someFunction() {
    // 使用 globalVar
}
​
// file2.cpp
extern int globalVar; // 不能访问 file1.cpp 中的 globalVar
2. 修饰局部变量

static 修饰的局部变量的生命周期会延长到整个程序的运行时间.

局部 static 变量在函数调用结束后不会被销毁,而是保留其值,直到下一次函数调用。

(非static修饰的局部变量在其函数运行结束之后被销毁)

void myFunction() {
    static int counter = 0; // 只初始化一次
    counter++;
    std::cout << "Counter: " << counter << std::endl;
}
​
int main() {
    myFunction(); // 输出:Counter: 1
    myFunction(); // 输出:Counter: 2
    myFunction(); // 输出:Counter: 3
}

函数内部定义的 static 局部变量的作用范围是限于该函数内的

这意味着你不能直接从函数外部访问或修改这些 static 局部变量。

然而,有几种方法可以间接地访问或获取这些 static 局部变量的值:

1. 通过函数访问

你可以通过一个公共接口函数来访问或操作 static 局部变量。例如,你可以定义一个函数返回 static 局部变量的值,或者一个函数用于修改这个值。

#include <iostream>
​
int getCounter() {
    static int counter = 0; // static 局部变量
    return counter;
}
​
void incrementCounter() {
    static int counter = 0; // static 局部变量
    counter++;
}
​
int main() {
    incrementCounter();
    std::cout << "Counter: " << getCounter() << std::endl; // 输出:Counter: 1
​
    incrementCounter();
    std::cout << "Counter: " << getCounter() << std::endl; // 输出:Counter: 2
​
    return 0;
}

在这个例子中,getCounterincrementCounter 函数用于访问和修改 static 局部变量 counter 的值。

2. 通过类的静态成员

如果你需要从函数外部获取类似功能的变量,你可以考虑将这个 static 变量放在类的静态成员中。这样,你可以通过类的静态成员函数访问或修改它。

#include <iostream>
​
class Counter {
public:
    static int getCounter() {
        return counter;
    }
​
    static void incrementCounter() {
        counter++;
    }
​
private:
    static int counter; // 静态成员变量
};
​
// 静态成员变量的定义
int Counter::counter = 0;
​
int main() {
    Counter::incrementCounter();
    std::cout << "Counter: " << Counter::getCounter() << std::endl; // 输出:Counter: 1
​
    Counter::incrementCounter();
    std::cout << "Counter: " << Counter::getCounter() << std::endl; // 输出:Counter: 2
​
    return 0;
}

在这个例子中,counter 是一个静态成员变量,getCounterincrementCounter 是静态成员函数,用于访问和修改这个变量。

注意:访问静态成员变量并不是必须为静态成员函数,也可以通过非静态成员函数访问。

但是,静态成员函数不能访问非静态成员变量。

3. 修饰函数

static 修饰一个函数时,这个函数的作用范围被限制在定义它的源文件中。

这在实现文件内部的帮助函数时很有用,防止函数名冲突。

// file1.cpp
static void helperFunction() {
    // 内部使用的辅助函数
}
​
// file2.cpp
void anotherFunction() {
    // 无法调用 file1.cpp 中的 helperFunction
}

4. 修饰类中的成员

在类中,static 修饰符用来定义类的静态成员。静态成员属于整个类,而不是某个对象。

它们在所有对象间共享,且可以通过类名直接访问同一个变量的地址空间

class MyClass {
public:
    static int staticVar; // 静态成员变量
    static void staticMethod() { // 静态成员函数
        std::cout << "Static method called!" << std::endl;
    }
};
​
// 静态成员变量的定义
int MyClass::staticVar = 0;
​
int main() {
    MyClass::staticVar = 10; // 通过类名访问
    MyClass::staticMethod(); // 调用静态方法
}

二、在类中的应用场景

主要用于管理类级别的成员变量和函数。以下是一些常见的应用场景:

1. 共享数据(跨对象共享状态)

在某些情况下,多个对象需要共享相同的数据。

通过将数据声明为静态成员变量,所有对象都可以访问和修改这个共享数据。例如,计数类实例的数量、共享配置参数等。

class Counter {
public:
    Counter() {
        ++count; // 增加实例计数
    }
​
    static int getCount() {
        return count;
    }
​
private:
    static int count; // 静态成员变量,用于计数
};
​
int Counter::count = 0;
​
int main() {
    Counter c1;
    Counter c2;
​
    std::cout << "Number of Counter objects: " << Counter::getCount() << std::endl;
    return 0;
}

在这个例子中,无论创建多少个 Counter 对象,静态变量 count 都能跟踪当前对象的数量。

2. 单例模式

单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。

静态成员变量和静态成员函数可以实现这一模式。

class Singleton {
public:
    // 静态成员函数,返回Singleton类的唯一实例
    static Singleton& getInstance() {
        // 静态局部变量,保证在函数首次调用时初始化,并且在程序生命周期内只初始化一次
        static Singleton instance;
        return instance; // 返回该唯一实例的引用
    }
​
private:
    // 私有构造函数,防止类外部直接创建对象实例
    Singleton() {}
​
    // 删除拷贝构造函数,防止通过拷贝创建多个实例
    Singleton(const Singleton&) = delete;
​
    // 删除赋值操作符,防止通过赋值操作创建多个实例
    Singleton& operator=(const Singleton&) = delete;
};
​
int main() {
    // 获取Singleton类的唯一实例
    Singleton& s1 = Singleton::getInstance();
​
    // 再次获取Singleton类的唯一实例
    Singleton& s2 = Singleton::getInstance();
​
    // s1 和 s2 是同一个实例,验证了单例模式的正确性
}

3. 工具类或辅助函数

有些函数不需要访问类的成员变量,而是执行与类相关的某些操作,这些函数可以被声明为静态成员函数。例如,数学计算、字符串操作等函数。

class MathUtils {
public:
    static int add(int a, int b) {
        return a + b;
    }
​
    static int multiply(int a, int b) {
        return a * b;
    }
};
​
int main() {
    int sum = MathUtils::add(3, 4);
    int product = MathUtils::multiply(3, 4);
​
    std::cout << "Sum: " << sum << ", Product: " << product << std::endl;
    return 0;
}

4. 类级别的常量

在某些情况下,类级别的常量需要在多个对象中使用,使用静态成员变量可以实现这一目的。

class Circle {
public:
    //constexpr 关键字修饰的变量:将该变量在编译时就可以计算出来,并且结果是一个常量
    //constexpr 函数:必须是一个单一的返回表达式,或者能够在编译时求值的逻辑。
    static constexpr double PI = 3.14159;
​
    Circle(double radius) : radius(radius) {}
​
    double getArea() const {
        return PI * radius * radius;
    }
​
private:
    double radius;
};
​
//计算圆形的面积
int main() {
    //设置私有变量的值
    Circle c1(10.0);
    std::cout << "Area: " << c1.getArea() << std::endl;
    return 0;
}

在这个例子中,PI 是类级别的常量,所有 Circle 对象都可以使用它。

5. 计数器或标识符生成器

静态成员变量可以用于实现全局唯一的计数器或标识符生成器,比如为每个对象分配一个唯一的 ID。

class IDGenerator {
public:
    IDGenerator() : id(nextID++) {}
​
    int getID() const 
    {
        return id;
    }
​
private:
    static int nextID; // 静态成员变量,用于生成唯一 ID
    int id;
};
​
int IDGenerator::nextID = 1;
​
int main() {
    IDGenerator obj1;
    IDGenerator obj2;
​
    std::cout << "ID of obj1: " << obj1.getID() << std::endl;
    std::cout << "ID of obj2: " << obj2.getID() << std::endl;
​
    return 0;
}

6. 缓存共享数据

静态成员变量可以用来缓存计算结果或共享数据,避免重复计算或加载。例如,在大型项目中,可能需要缓存配置数据或计算结果。

class Config {
public:
    static void loadConfig() {
        if (!configLoaded) {
            // 假设这里加载配置数据
            configLoaded = true;
            std::cout << "Configuration loaded." << std::endl;
        }
    }
​
private:
    static bool configLoaded; // 标志配置是否已经加载
};
​
bool Config::configLoaded = false;
​
int main() {
    Config::loadConfig(); // 第一次加载
    Config::loadConfig(); // 不会再次加载
​
    return 0;
}

const

一、修饰的变量和函数类型

const关键字在C++中用于表示不可修改的值或函数行为。

它可以修饰变量、成员变量、成员函数以及友元函数。下面详细解释它在这些不同场景中的作用:

1. const修饰的变量

  • 全局或局部变量: 使用const修饰的变量在其声明之后不能被修改。例如:

    const int a = 10;
    // a = 20; // 错误,a 是 const,不能修改

  • 指针变量: const可以修饰指针本身或者指针所指向的值,具体的用法如下:

    const int *ptr1 = &a;  // 指向 const int 的指针,不能通过 ptr1 修改 a
    int *const ptr2 = &a;  // const 指针,ptr2 指向的地址不能改变
    const int *const ptr3 = &a;  // 指向 const int 的 const 指针,ptr3 不能修改指向的地址或值

2. const修饰的成员变量

  • 类中的const成员变量: 使用const修饰的成员变量必须在初始化列表中初始化,且在对象的生命周期内不能被修改。例如:

    class MyClass {
    private:
        const int x;
    ​
    public:
        MyClass(int val) : x(val) {}  // x 必须在初始化列表中初始化
    };

3. const修饰的成员函数

  • const成员函数: const成员函数表示该函数不会修改类的成员变量(除了使用mutable修饰的成员变量)。

    这种函数只能调用其他const成员函数,不能调用非const成员函数。例如:

    class MyClass {
    private:
        int x;
    ​
    public:
        int getValue() const {  // 这是一个 const 成员函数
            // x++;  // 错误,不能在 const 成员函数中修改成员变量
            return x;
        }
    };

3. const修饰的返回值

  • 返回const对象: 如果一个成员函数返回类的对象或引用,并且希望确保返回的对象不会被修改,可以将返回类型声明为const

    class MyClass {
    private:
        int value;
    ​
    public:
        const int& getValue() const {
            return value;
        }
    };

    应用场景:

    • 当你希望调用者无法修改函数返回的对象时,可以使用const返回类型。这可以有效防止数据被意外篡改。

4. const修饰的常量表达式

  • constexprconst结合constexpr通常与const结合使用,表示在编译时就能确定的常量。这可以用于类的静态成员变量或函数中,提供常量表达式计算的能力。

    class MyClass {
    public:
        static constexpr int getConstant() {
            return 42;
        }
    };

    应用场景:

    • 当你需要在编译时确定常量值,并且希望该值在整个程序中保持不变时,constexprconst的结合是理想的选择。

5. const修饰的友元函数

友元函数中的const参数:

友元函数可以通过const参数来限制对对象的修改。

虽然友元函数本身不属于类成员,但它可以访问类的私有成员。因此,通过将参数声明为const,可以确保友元函数不会修改传入的对象。例如:

class MyClass {
private:
    int x;
​
public:
    MyClass(int val) : x(val) {}
​
    friend void printValue(const MyClass& obj);  // 友元函数,参数是 const 引用
};
​
void printValue(const MyClass& obj) {
    std::cout << obj.x << std::endl;
    // obj.x = 20;  // 错误,obj 是 const,不能修改
}

二、相关内容补充

友元函数的介绍

它虽然不是某个类的成员函数,但却能够访问该类的私有和保护成员。

友元函数的主要用途是实现需要直接访问类内部数据的全局函数或其他类的成员函数。

友元函数的特点和用法
  1. 友元函数的定义: 友元函数是在类内部通过关键字friend声明的,但它本身并不是类的成员。

    友元函数可以是普通的全局函数、其他类的成员函数,甚至是友元类的成员函数。

    class MyClass {
    private:
        int x;
    ​
    public:
        MyClass(int val) : x(val) {}
    ​
        friend void printValue(const MyClass& obj); // 声明友元函数
    };
    ​
    // 友元函数定义
    void printValue(const MyClass& obj) {
        std::cout << "Value of x: " << obj.x << std::endl; // 可以访问私有成员 x
    }

  2. 友元函数的访问权限: 友元函数能够访问类的所有成员,包括私有成员和保护成员。

  3. 友元函数不是成员函数: 友元函数并不是该类的成员函数,因此它不具有this指针。

    友元函数不依赖于对象来调用,它是一个独立的函数。

  4. 友元函数的常见应用场景:

    • 重载运算符: 友元函数常用于运算符重载,特别是需要两个不同类型的操作数时。例如,重载<<运算符输出对象的内容。

    • 实现跨类操作: 友元函数可以访问两个类的私有成员,在实现两个类之间的紧密协作时非常有用。

  5. 友元函数的声明与定义: 友元函数的声明通常放在类的内部,表示这个函数是该类的友元;

    而函数的定义则可以在类的外部实现。注意,友元函数的定义不需要使用friend关键字。

    class MyClass {
    private:
        int x;
    ​
    public:
        MyClass(int val) : x(val) {}
    ​
        friend void printValue(const MyClass& obj);  // 声明友元函数
    };
    ​
    // 友元函数的定义
    void printValue(const MyClass& obj) {
        std::cout << "Value: " << obj.x << std::endl;
    }
    ​
    int main() {
        MyClass obj(42);
        printValue(obj);  // 直接调用友元函数
        return 0;
    }

  6. 友元函数与类的耦合度: 虽然友元函数为跨类操作提供了方便,但也增加了类与函数之间的耦合度,因此使用友元函数时要注意适度,避免过多的友元声明导致代码的复杂性增加。

友元类

除了友元函数,C++还允许声明整个类作为另一个类的友元。

这意味着友元类的所有成员函数都可以访问另一个类的私有和保护成员。例如:

class ClassB;  // 前向声明
​
class ClassA {
private:
    int a;
​
public:
    ClassA(int val) : a(val) {}
​
    friend class ClassB;  // 声明 ClassB 为友元类
};
​
class ClassB 
{
public:
    void showA(const ClassA& obj) 
    {
        std::cout << "ClassA's private member a: " << obj.a << std::endl; // 可以访问 ClassA 的私有成员
    }
};
​
int main() {
    ClassA objA(10);
    ClassB objB;
    objB.showA(objA);  // 通过 ClassB 的成员函数访问 ClassA 的私有成员
    return 0;
}

constconstexpr的区别

1. 定义和目的
  • const

    • const用于定义常量,表示其值在程序运行期间不可改变。可以用于变量、对象、函数参数、返回类型等。

    • const声明的变量在运行时才确定具体的值。

    const int x = 10;  // x 是一个常量,值为 10

  • constexpr

    • constexpr用于定义一个在编译期就能确定其值的常量。它可以用于常量表达式计算,确保在编译阶段就可以确定结果。

    • constexpr可以用于函数、变量、构造函数等,确保它们能够在编译时计算出值。

    constexpr int y = 10;  // y 是一个编译时常量,值为 10

2. 编译时与运行时
  • const

    • const变量的值可以在运行时确定,也可以在编译时确定。

    • const变量不一定是编译期常量。

  • constexpr

    • constexpr要求变量在编译时确定值。

    • constexpr变量必须是编译期常量。

3. 函数使用
  • const函数

    • const可以修饰类的成员函数,表示该函数不会修改类的成员变量。

    class MyClass {
    public:
        int getValue() const {
            return value;
        }
    private:
        int value;
    };

  • constexpr函数

    • constexpr函数要求在编译时即可执行,且返回值必须是编译期常量。它可以被用于常量表达式中。

    constexpr int square(int x) {
        return x * x;
    }
    constexpr int y = square(5);  // y 是一个编译时常量

4. 使用场景
  • const

    • 适用于需要在运行时或编译时确保值不变的场景。const常用于保护不应被修改的变量或参数。

  • constexpr

    • 适用于需要在编译时计算出结果的场景,例如用于常量表达式,数组大小,或模板参数等。constexpr能提高程序的运行效率,因为它能在编译时完成一些计算。

5. 构造函数
  • const

    • const不用于构造函数。

  • constexpr

    • constexpr构造函数用于定义一个类,该类的对象可以是编译时常量。

    struct Point {
        int x, y;
        constexpr Point(int a, int b) : x(a), y(b) {}
    };
    constexpr Point p(10, 20);  // p 是一个编译时常量对象

标签:友元,const,函数,int,成员,C++,static
From: https://blog.csdn.net/m0_63956046/article/details/141473138

相关文章

  • 【C++基础】类的构造函数和析构函数
    目录构造函数(Constructor)定义种类1.默认构造函数2.带参数的构造函数3.浅拷贝构造函数4.深拷贝构造函数深拷贝和浅拷贝的区别5.移动构造函数析构函数(Destructor)构造函数与析构函数的调用时机构造函数:析构函数:构造函数和析构函数的最佳实践避免在析构函数中抛出异......
  • 【C++】12.智能指针
    在上一篇博客【C++】11.异常中我们知道有些时候会造成内存空间的未释放从而导致内存泄漏,因此本篇博客的内容就是如何减少内存泄漏——智能指针。一、RAIIRAII(ResourceAcquisitionIsInitialization)是一种利用对象生命周期来控制程序资源的简单技术,因此又被称为资源获取......
  • C++ 使用 using 声明语句放开基类的重载函数
    #include<functional>#include<string>#include<iostream>#include<vector>#include<memory>#include<set>#include<map>usingnamespacestd;namespace{classAnimal{public:stringGe......
  • C++ STL源码个人学习与分析记录 ——空间配置器(allocator)
    STL源码个人学习与分析记录——空间配置器(allocator)1.为什么需要空间配置器?2.SGI-STL空间配置器的实现2.1一级空间配置器:malloc_alloc_template2.2二级空间配置器:default_alloc_template2.2.1.内存池技术2.2.2.自由链表(free-list)2.2.3Union2.3二级空间配置器的......
  • C++ STL源码个人学习与分析记录 ——Construct()与Destroy()
    STL源码个人学习与分析记录——Construct()与destroy()1.目前所使用的编译器1.1编译器:MinGWVersion:13.2.01.2MinGW的主要组件1.3写文初衷2.构造与析构工具:Construct()与Destory()函数的定义2.1Construct()函数的定义2.1.1“__cplusplus”的含义2.1.2编译组态2.1.3(void......
  • C++(time_t)
    目录1.数据类型:2.使用场景:3.常见函数:4.时间的存储和表示:5.示例:6.注意事项:在C++中,time_t是一种数据类型,用于存储日历时间。它定义在<ctime>头文件中。time_t本质上是一个算术类型,通常是整数或浮点数,用于表示从标准纪元(通常是1970年1月1日00:00:00UTC,称为“Unixepo......
  • 小白学习c++{复习篇}P11【2066】买图书(c++)详细讲解
    目录EXTRA##PT1-代码呈现awaPT2-问题填空qwqPT3-课后小结......
  • C++的动态数组vector番外之capacity
    今日诗词:爱他明月好,憔悴也相关。西风多少恨,吹不散眉弯。                    ——《临江仙·寒柳》【清】纳兰容若目录引言正文string中的和vector中的capacity有什么区别 vector扩容时内存分配的策略是什么?capacity在vect......
  • Java之static静态代码块和方法
    文章目录一:static关键字二:静态变量三:静态方法四:静态代码块五:总结一:static关键字用于定义类级别的属性和方法,这些属性和方法属于类本身,而不是类的任何特定实例对象static修饰的方法或变量,优先于对象执行,所以内存会先有static修饰的内容,后有对象的内容static关键字......
  • 一文彻底了解ES6中的var、let、const基本用法以及暂时性死区和变量提升的区别
             ECMAScript变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。有三个关键字可以声明变量:var、let、const。其中var在ECMAScript的所有版本中都可以使用,而 const和let只能在ECMAScript6及更......