static、const、final、explicit等关键字(常更新)

static

1、静态成员变量

静态成员变量在类声明之后、类外进行定义并初始化,初始化完成后,非const类型的静态成员变量的值在程序运行时可以被改变,而const类型的静态成员变量其值则固定不变。

  • 所有对象共享同一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化

2、静态成员函数

  • 所有对象共享同一个函数
  • 只能访问静态成员变量
  • 不可以访问非静态成员变量

3、静态全局变量

  • 如果全局变量被声明为静态,它的作用域将被限制在定义它的源文件中。这意味着,尽管静态全局变量在整个程序执行期间都存在,但它只对定义它的文件是可见的,其他源文件中的代码无法直接访问它。这有助于避免命名冲突并减少全局变量的副作用。
  • 生命周期:整个程序运行期间。
  • 作用域:定义它的源文件内。

4、静态普通函数

  • 在C++中,静态函数还可以用于限制函数的作用域到定义它的源文件中,类似于静态全局变量。这意味着即使在不同的源文件中有同名函数,只要它们是静态的且位于不同文件,就不会产生链接错误。
  • 作用域:定义它的源文件内。
  • 静态普通函数通常用于实现一些全局性的功能,这些功能与特定的类或对象实例无关,但又希望保持其作用域限制在较小范围内,以减少对外部的影响或避免命名冲突。例如,某些辅助工具函数、日志记录函数或者配置文件的读取函数等,可能适合以静态普通函数的形式实现。

静态普通函数是C++中一种用于实现特定功能、限制作用域同时具有较长生命周期的函数,适用于那些不需要与特定对象交互的通用逻辑或工具方法。

5、静态局部变量

  • static用于局部变量时,它改变了变量的生命周期。通常,局部变量在函数调用结束后会被销毁,但静态局部变量则会在第一次初始化后持续整个程序的生命周期,即使函数结束调用后依然存在。
  • 它的作用域仍然限于定义它的函数内部。
  • 生命周期:程序运行期间一直存在。
  • 作用域:定义该变量的函数内部。

const

const首先作用于左边,若左边没有东西,就作用于右边

指针常量与常量指针在C++中是两个容易混淆的概念,但它们有着本质的区别:

指针常量(Pointer Constants)

指针常量是指一个指针变量,其自身的地址(即指向的位置)在初始化后就不能再改变。换句话说,这个指针一旦指向了一个内存地址,之后就不能再让它指向另一个地址。但是,指针所指向的数据是可以修改的(如果该数据不是常量的话)。在声明时,关键字const位于星号(*)的右侧,靠近指针变量名。

语法示例:

int* const ptr = &someValue;

在这个例子中,ptr是一个指向整数的指针常量,它初始化后就不能再指向其他地方,但是可以通过ptr修改someValue的值(如果someValue不是常量)。

常量指针(Constant Pointers)

常量指针是指向常量的指针,即通过这个指针不能修改它所指向的数据,但可以改变指针自身指向的地址。这意味着,虽然指针指向的内存位置的内容是不可变的,但指针可以被重新设置去指向另一个内存位置。在声明时,关键字const位于星号(*)的左侧,靠近数据类型。

语法示例:

const int* ptr = &someValue;

这里,ptr是一个常量指针,它指向的整数是不可修改的,但ptr可以被重新指向另一个整数的地址。

指向常量的常量指针(Constant Pointer to Constant)

这种情况下,指针既不能指向新的地址,也不能修改它所指向的数据。

语法示例:

const int* const ptr = &someValue; // ptr和ptr指向的int都是常量

在这个例子中,ptr是一个指向常量整数的常量指针,既不能改变它所指向的地址,也不能通过它修改someValue

const与函数

在C++中,const关键字有多种用途,其中之一是在函数声明和定义中使用,以指明该函数不会修改类的任何成员变量(对于成员函数而言)或者输入参数(对于普通函数而言)。这种情况下,const有助于表达函数的意图,提高代码的可读性和安全性。下面分别从成员函数和普通函数两个角度来讲解const在只读函数中的应用。

成员函数中的const
  1. 常量成员函数:当一个成员函数被声明为const时,表明这个函数不会修改类的任何成员变量(除了那些用mutable关键字标记的成员)。这为调用者提供了一个保证,即这个操作是安全的,不会影响对象的状态。
class MyClass {
public:
    void display() const { // 常量成员函数
        cout << "Value: " << value << endl;
    }

    int getValue() const { // 另一个常量成员函数示例
        return value;
    }

private:
    int value;
};


在上述例子中,display()getValue()都是常量成员函数,它们不会修改MyClass对象的状态。

  1. const后置修饰符:在C++中,成员函数的const修饰符习惯上放置在函数参数列表之后,这是C++的一个特殊语法约定。尽管它看起来像是返回类型的一部分,但实际上它修饰的是函数本身,表明这是一个“不会改变对象状态”的版本。
普通函数中的const

在非成员函数(即普通函数)中,const关键字通常用于指针或引用类型的参数,以表明该参数在函数内部会被当作常量处理,不会被修改。

void printValue(const int& val) { // val不会被修改
    cout << "Value: " << val << endl;
}

void modifyArray(int* arr, size_t size) {
    for(size_t i = 0; i < size; ++i) {
        arr[i] *= 2; // 修改数组内容
    }
}

void printArray(const int* arr, size_t size) { // 指向const的指针,arr内容不可修改
    for(size_t i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

在上面的例子中,printValue接受一个指向整数的常量引用,保证了传入的值不会被函数修改。同样地,printArray接收一个指向const整数的指针,表明它只读取数组内容,不会进行修改。

总结

通过在函数声明和定义中使用const关键字,可以明确地告诉编译器及程序员哪些函数或参数是只读的,这对于维护代码的稳定性和理解代码逻辑非常有帮助。编译器也会根据这些信息进行类型检查,确保不违反const限制,从而减少潜在的错误。

  • 指针常量关注的是指针本身不可变,即指针的地址不可更改。
  • 常量指针关注的是指针所指向的数据不可变,但指针本身可以指向其他地方。
  • 指向常量的常量指针(Constant Pointer to Constant):指针指向的数据不可变,同时指针自身也不可变。
  • 修饰函数的参数,表示该参数不可更改。
  • 修饰类的成员函数,表示该成员函数不能修改任何成员变量。
  • 修饰类的成员变量,表示该成员不能被修改,且只能通过初始化列表的方式初始化。
  • 修饰类的对象的整体,也即常量对象,表示该对象的所有成员变量都不能被修改,且只能调用被const修饰的成员函数。

explicit

在C++中,explicit关键字主要用于限制编译器执行隐式类型转换的能力,特别是在单个参数的构造函数或者转换函数上。它的主要目的是为了防止意外的、不易察觉的类型转换,提高代码的清晰度和安全性。下面详细解释explicit的使用场景和作用。

使用场景

  1. 构造函数:当一个类的构造函数只有一个参数时,编译器会自动允许将该类型的值隐式转换为类的对象。这在某些情况下可能导致意料之外的转换。通过将这样的构造函数声明为explicit,你可以禁止这种隐式转换。
class Inch {
public:
    explicit Inch(int i) : value(i) {} // explicit构造函数
private:
    int value;
};

void processInch(Inch i) {}

int main() {
    // 下面的语句是错误的,因为不能隐式转换
    // processInch(5); 
    // 必须显式构造Inch对象
    processInch(Inch(5));
}

  1. 转换函数:类似地,如果一个类定义了转换运算符(conversion operator)到其他类型,也可以使用explicit关键字来限制隐式转换。
class Integer {
public:
    explicit operator double() const { return value; }
private:
    int value;
};

作用

  • 避免误用:通过阻止不必要的隐式类型转换,explicit关键字可以帮助预防编程错误,比如错误的数据类型假设。
  • 提高代码可读性:它使得类型转换更加显式,让阅读代码的人更容易理解类型转换的意图,增加了代码的可读性和可维护性。
  • 性能考量:虽然这不是explicit的主要目的,但在某些情况下,避免无意识的构造和转换操作可能对性能有正面影响,尤其是当这些操作成本较高时。

总的来说,explicit关键字是一个强大的工具,用于控制和限制C++中的类型转换,帮助开发者编写更安全、更易于理解和维护的代码。

final

final是一个关键字,用于修饰类或成员函数,以防止它们被继承或重写。

  1. 修饰类:当final关键字用于修饰一个类时,这意味着该类不能被其他类继承。任何尝试继承final修饰的类的操作都会导致编译错误。
    示例:
class Base final {
    // ...
};
class Derived : public Base { // 编译错误,Base是final的
    // ...
};

  1. 修饰成员函数:当final关键字用于修饰一个成员函数时,这意味着该函数在派生类中不能被重写。任何尝试重写final修饰的成员函数的操作都会导致编译错误。
    示例:
class Base {
public:
    virtual void func() final {
        // ...
    }
};
class Derived : public Base {
public:
    void func() override { // 编译错误,func是final的
        // ...
    }
};

使用final关键字可以增强代码的安全性,因为它可以防止意外的继承或重写,从而确保类的行为符合设计者的预期。

// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 #pragma once #include <cstdint> #include <cstring> #include "opentelemetry/nostd/span.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace trace { // TraceId represents an opaque 128-bit trace identifier. The trace identifier // remains constant across the trace. A valid trace identifier is a 16-byte array with at // least one non-zero byte. class TraceId final { public: // The size in bytes of the TraceId. static constexpr int kSize = 16; // An invalid TraceId (all zeros). TraceId() noexcept : rep_{0} {} // Creates a TraceId with the given ID. explicit TraceId(nostd::span<const uint8_t, kSize> id) noexcept { memcpy(rep_, id.data(), kSize); } // Populates the buffer with the lowercase base16 representation of the ID. void ToLowerBase16(nostd::span<char, 2 * kSize> buffer) const noexcept { constexpr char kHex[] = "0123456789abcdef"; for (int i = 0; i < kSize; ++i) { buffer[i * 2 + 0] = kHex[(rep_[i] >> 4) & 0xF]; buffer[i * 2 + 1] = kHex[(rep_[i] >> 0) & 0xF]; } } // Returns a nostd::span of the ID. nostd::span<const uint8_t, kSize> Id() const noexcept { return nostd::span<const uint8_t, kSize>(rep_); } bool operator==(const TraceId &that) const noexcept { return memcmp(rep_, that.rep_, kSize) == 0; } bool operator!=(const TraceId &that) const noexcept { return !(*this == that); } // Returns false if the TraceId is all zeros. bool IsValid() const noexcept { return *this != TraceId(); } // Copies the opaque TraceId data to dest. void CopyBytesTo(nostd::span<uint8_t, kSize> dest) const noexcept { memcpy(dest.data(), rep_, kSize); } private: uint8_t rep_[kSize]; }; } // namespace trace OPENTELEMETRY_END_NAMESPACE
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值