相关文章推荐
文质彬彬的豆芽  ·  阿里达摩院获KDD ...·  3 月前    · 
憨厚的金鱼  ·  DAX 术语表 - DAX | ...·  1 年前    · 
痴情的领结  ·  c++ - Using ...·  1 年前    · 
豪爽的牛肉面  ·  用 Python 解析 HTML ...·  1 年前    · 
    static inline const string TOP_LEVEL_DOMAIN_STR{
      "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])" };

参考<c++17入门经典> 11章,Static Constants。

c++类中的常量

定义属于这个类范围的常量

class test
private:
    enum {Months = 12};

  这种声明枚举不会创建类数据成员,这里枚举只是为了创建类数据成员,因此不用提供枚举名。类似上面的例子还有ios_base::fixed等。

扩充:c++11作用域内的枚举

enum egg {Small, Medium, Large, Jumbo};
enum t_shirt {Small, Medium, Large, Xlarge};

  编译器提示重复定义SmallMediumLargeJumbo。因为egg Small和t_shirt Small位于相同的作用域内。
  c++11提供了一种新的枚举,它的作用域为类。可以使用关键字class或者struct

enum class egg {Small, Medium, Large, Jumbo};
enum class t_shirt {Small, Medium, Large, Xlarge};
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;

const常量

class test
private:
    const int n;
public:
    test():n(100){}

  类的声明只是声明了类的形式,并没有创建对象,因此,在创建对象前,将没有用于储存值的空间。

c++98与c++11的区别
  在C++98标准里,只有static const声明的整型成员能在类内部初始化,并且初始化值必须是常量表达式。这些限制确保了初始化操作可以在编译时期进行。

class X {
    static const int m1 = 7;   // 正确
    const int m2 = 7;    // 错误:无static
    static int m3 = 7;              // 错误:无const
    static const string m5 = “odd”; //错误:非整型

  C++11的基本思想是,允许非静态(non-static)数据成员在其声明处(在其所属类内部)进行初始化。这样,在运行时,需要初始值时构造函数可以使用这个初始值。现在,我们可以这么写:

class A {
public:
    int a = 7;
//它等同于使用初始化列表:
class A {
public:
    int a;
    A() : a(7) {}

  c++11这样的好处就是当构造函数需要多个初始化时就会变得很简洁。

const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。

    (1)不能在类声明中初始化const数据成员。

以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。

    class A

        const int SIZE = 100;     // 错误,企图在类声明中初始化const数据成员

        int array[SIZE];        // 错误,未知的SIZE

(2)const数据成员的初始化只能在类构造函数的初始化表中进行

    class A

        A(int size);     // 构造函数

        const int SIZE ;

    A::A(int size) : SIZE(size)    // 构造函数的初始化表

    A a(100); // 对象 a 的SIZE值为100

    A b(200); // 对象 b 的SIZE值为200

    (3)怎样才能建立在整个类中都恒定的常量呢?别指望const数据成员了,应该用类中的枚举常量来实现。

    class A

        enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量

        int array1[SIZE1];

        int array2[SIZE2];

    枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如PI=3.14159)。

//------------------------------------------------------------------------

如果你想得到一个可用于常量表达式中的常量,例如数组大小的定义,那么你有两种选择:

 class X {
  static const int c1 = 7;
  enum { c2 = 19 };
  char v1[c1];
  char v2[c2];
  // ...
一眼望去,c1的定义似乎更加直截了当,但别忘了只有static const的整型或枚举型量才能如此初始化。
这就很有局限性,例如
 class Y {
  const int c3 = 7;  // error: not static
  static int c4 = 7;  // error: not const
  static const float c5 = 7; // error not integral

static变量

  static变量不像普通的变量,static变量独立于一切类对象处在。static修饰的变量先于对象存在,所以static修饰的变量要在类外初始化。因为static是所有对象共享的东西嘛,必须要比对象先存在的。

初始化:数据类型 类名::静态数据成员名=值;

1、初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆

2、初始化时不加该成员的访问权限控制符private、public等

3、初始化时使用作用域运算符来表明它所属的类,因此,静态数据成员是类的成员而不是对象的成员。

class test
private:
public:
    static int i;
int test::i = 100;//此句包含了声明和赋值,初始化不受private和protected访问限制,但是若是priivate,下面main函数就无法访问
int main()
    cout << test::i << endl;
    return 0;

  好处:用static修饰的成员变量在对象中是不占内存的,因为他不是跟对象一起在堆或者栈中生成,用static修饰的变量在静态存储区生成的,所以用static修饰一方面的好处是可以节省对象的内存空间。所以一般类const变量一般改为static const变量,可以节省一些空间。

const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间。

static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate = 2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static

在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。

#ifdef A_H_
#define A_H_
#include <iostream>
usingnamespace std;
class A{
public:
    A(int a);
    staticvoid print();//静态成员函数
private:
    static int aa;//静态数据成员的声明
    staticconst int count;//常量静态数据成员(可以在构造函数中初始化)
    const int bb;//常量数据成员
int A::aa=0;//静态成员的定义+初始化
const int A::count=25;//静态常量成员定义+初始化
A::A(int a):bb(a){//常量成员的初始化
aa+=1;
void A::print(){
cout<<"count="<<count<<endl;
cout<<"aa="<<aa<<endl;
#endif
void main(){
    A a(10);
    A::print();//通过类访问静态成员函数
    a.print();//通过对象访问静态成员函数

https://www.cnblogs.com/fuao2000/p/11006999.html

C++ constexpr类型说明符

字面值类型

常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为“字面值类型”(literal type)。

算术类型、引用和指针都属于字面值类型。某些类也是字面值类型,它们可能含有constexpr函数成员。自定义类Sales_item、IO库、string类型不属于字面值类型。

尽管指针和引用可以定义成constexpr,但它们的初始值受到严格限制。一个constexpr指针的初始值必须是nullptr、0或存储于某个固定地址中的对象。

函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。定义于函数体外的对象其地址固定不变,能用来初始化constexpr指针。允许函数定义一类有效范围超出函数本身的变量,如局部静态变量,这类变量和定义在函数体之外的变量一样有固定地址,因此constexpr引用能绑定到这样的变量上,constexpr指针也能指向这样的变量。

聚合类(aggregate class)允许利用者直接访问其成员,并且具有特殊的初始化形式。聚合类满足以下条件:

所有成员都是public的

没有定义构造函数

没有类内初始值

没有基类,也没有虚函数

怎么理解呢?

首先,看来看去聚合类其实就是一个C结构体;其次,聚合这个词,应该是相对组合的,表明了成员和类之间的松散关系。

当一个类是聚合类时,就可以使用初始值列表像下面这样初始化了:

struct Point{
    int x;
    int y;
};
Point pt = {10, 10};


字面值常量类

前面讲过constexpr函数,它的参数和返回值都必须是常量表达式。而常量表达式的最基本要素就是字面值类型。字面值类型除了包括算数类型,引用和指针以外,某些类也属于字面值类型,C++11称之为字面值常量类。主要包括两种情况:

首先数据成员都是字面类型的聚合类就是一种。上面的Point类就是一个例子。我们可以这样理解,字面值的聚合继续具有字面值的特征,这里主要是编译阶段可以求值。

还有一种情况,虽然不是聚合类,但是只要满足下面的条件,也是字面值常量类:

数据成员必须都必须是字面值类型。

类必须至少含有一个constexpr构造函数。

如果一个数据成员含有类内初始值,则初始值必须是常量表达式;如果成员属于某种类,初始值必须使用该类的constexpr构造函数。

类必须使用析构函数的默认定义。

对于这几个条件,作者这样理解:

满足条件1,就可以在编译阶段求值,这一点和聚合类一样。

满足条件2,就可以创建这个类的constexpr类型的对象。

满足条件3,就可以保证即使有类内初始化,也可以在编译阶段解决。

满足条件4,就可以保证析构函数没有不能预期的操作。

constexpr构造函数

通过前置constexpr关键字,就可以声明constexpr构造函数,同时:

除了声明为=default或者=delete以外,constexpr构造函数的函数体一般为空,使用初始化列表或者其他的constexpr构造函数初始化所有数据成员。

struct Point{
    constexpr Point(int _x, int _y)
        :x(_x),y(_y){}
    constexpr Point()
        :Point(0,0){}
    int x;
    int y;
};

constexpr Point pt = {10, 10};


这样声明以后,就可以在使用constexpr表达式或者constexpr函数的地方使用字面值常量类了。

关键字 constexpr 于 C++11 中引入并于 C++14 中得到改善。它表示常数表达式。与 const 相同,它可应用于变量,因此如果任何代码试图修改该值,均将引发编译器错误。与 const 不同,constexpr 也可应用于函数和类构造函数。 constexpr 指示值或返回值是常数,并且如果可能,将在编译时计算值或返回值。

constexpr变量

  • 在一个复杂的系统中,很难分辨一个初始值到底是不是常量表达式。当然可以定义一个const变量并把它的初始值设为我们认为的某个常量表达式,但在实际使用时,尽管要求如此却常常发现初始值并非常量表达式的情况(例如上述的const int sz = get_size(); 语句)。
  • C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:
  • const 和 constexpr 变量之间的主要区别在于:const 变量的初始化可以延迟到运行时,而 constexpr 变量必须在编译时进行初始化。所有 constexpr 变量均为常量,因此必须使用常量表达式初始化。
  • 尽管不能使用普通函数作为constexpr变量的初始值,但可以通过constexpr函数(编译时就可以计算其结果)初始化constexpr变量。
  • constexpr 函数

    constexpr 函数是在使用需要它的代码时,可以在编译时计算其返回值的函数。当其参数为 constexpr 值并且在编译时使用代码需要返回值时(例如,初始化一个 constexpr 变量或提供一个非类型模板参数),它会生成编译时常量。使用非constexpr 参数调用时,或编译时不需要其值时,它将与正则函数一样,在运行时生成一个值。

    遵循以下规定:(1)函数的返回类型以及所有形参的类型都得是字面值类型;(2)函数体中必须只有一条return语句。

    代码说明:

    以上代码演示了如何在编译期计算3的阶乘。
    在C++11之前,在编译期进行数值计算必须使用模板元编程技巧。具体来说我们通常需要定义一个内含编译期常量value的类模板(也称作元函数)。这个类模板的定义至少需要分成两部分,分别用于处理一般情况和特殊情况。
    代码示例中Factorial元函数的定义分为两部分:
    当模板参数大于0时,利用公式 N!=N*(N-1)! 递归调用自身来计算value的值。
    当模板参数为0时,将value设为1这个特殊情况下的值。
    在C++11之后,编译期的数值计算可以通过使用constexpr声明并定义编译期函数来进行。相对于模板元编程,使用constexpr函数更贴近普通的C++程序,计算过程显得更为直接,意图也更明显。
    但在C++11中constexpr函数所受到的限制较多,比如函数体通常只有一句return语句,函数体内既不能声明变量,也不能使用for语句之类的常规控制流语句。
    如factorial函数所示,使用C++11在编译期计算阶乘仍然需要利用递归技巧。
    C++14解除了对constexpr函数的大部分限制。在C++14的constexpr函数体内我们既可以声明变量,也可以使用goto和try之外大部分的控制流语句。
    如factorial2函数所示,使用C++14在编译期计算阶乘只需利用for语句进行常规计算即可。
    虽说constexpr函数所定义的是编译期的函数,但实际上在运行期constexpr函数也能被调用。事实上,如果使用编译期常量参数调用constexpr函数,我们就能够在编译期得到运算结果;而如果使用运行期变量参数调用constexpr函数,那么在运行期我们同样也能得到运算结果。
    代码第32行所演示的是在运行期使用变量n调用constexpr函数的结果。
    准确的说,constexpr函数是一种在编译期和运行期都能被调用并执行的函数。出于constexpr函数的这个特点,在C++11之后进行数值计算时,无论在编译期还是运行期我们都可以统一用一套代码来实现。编译期和运行期在数值计算这点上得到了部分统一。

    constexpr和指针

    还记得const与指针的规则吗?如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针本身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

    与const不同,在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关。
     

    https://blog.csdn.net/craftsman1970/article/details/80244873

    https://blog.csdn.net/weixin_40087851/article/details/82754189

    https://blog.csdn.net/YhL_Leo/article/details/50864210

    https://blog.csdn.net/zwvista/article/details/54429416