自学内容网 自学内容网

嵌入式学习-C嘎嘎-Day02

1. 类和对象(class and object/instance)

类是一个抽象的概念,规定了对象的行为和数据,对象是实体,需要根据类的概念进行创建。

类规定了对象的行为和数据,正式的说法应该是:

  • 成员函数(行为):表示对象可以调用的函数,完成一些功能操作,通常使用动词或动词词组命名,比如跑、跳、吃饭、打游戏等......
  • 属性/成员变量(数据):表示对象存储的数值,通常使用名词或名词词组命名,比如颜色、身高、价格等.......

成员member(数据成员) = 成员函数 + 成员变量

【例子】以手机为例,来说明类和对象的使用。

C++类命名规范:

自定义类的命名需要遵守帕斯卡命名法(大驼峰命名法):所有单词的首字母大写;

成员使用下划线命名法:所有单词消息,中间使用下划线分割。

#include <iostream>

using namespace std;

/**
 * @brief The MobilePhone class
 * 手机类
 */
class MobilePhone
{
public: // 公有权限:完全开放
    // 品牌、型号、价格
    string brand;
    string model;
    double price;

    // 播放音乐、运行游戏、通信
    void play_music()
    {
        cout << "迎面走来的你让我蠢蠢欲动" << endl;
    }

    void run_game()
    {
        cout << "原神,启动!" << endl;
    }

    void communicate()
    {
        cout << "你好吗?" << endl;
    }
};

int main()
{


    return 0;
}

基于上面的手机类,完成手机对象的创建。

C++拥有两种对象:

  • 栈内存对象

栈内存对象的生命周期与局部变量相同,所在的{}执行完成后,对象自动被销毁,可以搭配引用使用。

  • 堆内存对象

必须使用new关键字创建对象,必须使用指针存储对象地址,调用成员时必须使用->,而不是.

如果不手动使用delete销毁,则对象持续存在(内存泄漏)。delete之后就不能继续使用对象了。

#include <iostream>

using namespace std;

/**
 * @brief The MobilePhone class
 * 手机类
 */
class MobilePhone
{
public: // 公有权限:完全开放
    // 品牌、型号、价格
    string brand;
    string model;
    double price;

    // 播放音乐、运行游戏、通信
    void play_music()
    {
        cout << "迎面走来的你让我蠢蠢欲动" << endl;
    }

    void run_game()
    {
        cout << "原神,启动!" << endl;
    }

    void communicate()
    {
        cout << "你好吗?" << endl;
    }
};

int main()
{
    // 创建一个栈内存对象
    MobilePhone mp1;
    // 写入成员变量
    mp1.brand = "Apple";
    mp1.model = "16Pro Max";
    mp1.price = 9999;
    // 获取成员变量值
    cout << mp1.brand << endl;
    cout << mp1.model << endl;
    cout << mp1.price << endl;
    // 调用成员函数
    mp1.communicate();
    mp1.play_music();
    mp1.run_game();

    // 创建一个堆内存对象
    MobilePhone* mp2 = new MobilePhone;
    // 写入成员变量
    mp2->brand = "小米";
    mp2->model = "15";
    mp2->price = 4999;
    // 读取成员变量
    cout << mp2->brand << endl;
    cout << mp2->model << endl;
    cout << mp2->price << endl;
    // 调用成员函数
    mp2->communicate();
    mp2->play_music();
    mp2->run_game();
    // 手动销毁
    delete mp2;

    return 0;
} // mp1自动被销毁

2. 封装

上面的类与结构体很相似,实际上类需要进行封装,封装可以让类把一些属性和细节隐藏重新提供给外部相应的调用接口,封装可以提升类的安全性和可维护性,也可以让程序员专注于类的整体而非内部细节。

常用的接口分为读(getter)和写(setter),可以根据不同成员变量的读写需求添加接口,例如某变量只需要外部读取,则只添加getter;某变量需要外部读写,则需要同时添加getter和setter......

#include <iostream>

using namespace std;

class MobilePhone
{
private:    // 私有权限:只能在类内访问
    string brand = "山寨"; // 只读
    string model; // 可读可写
    double price; // 只写

public:
    string get_brand()
    {
        return brand;
    }

    string get_model()
    {
        return model;
    }

    void set_model(string m)
    {
        model = m;
    }

    void set_price(double p)
    {
        price = p;
    }
};

int main()
{
    MobilePhone mp1;
    mp1.set_model("16Pro Max");
    mp1.set_price(9999);
    cout << mp1.get_brand() << endl;
    cout << mp1.get_model() << endl;

    MobilePhone* mp2 = new MobilePhone;
    mp2->set_model("15");
    mp2->set_price(4999);
    cout << mp2->get_brand() << endl;
    cout << mp2->get_model() << endl;
    delete mp2;

    return 0;
} // mp1自动被销毁

3. 构造函数 constructor

3.1 基本使用

创建一个对象必须调用构造函数,如果程序员不手写构造函数,编译器会自动添加一个没有参数且函数体为空的构造函数。

构造函数在结构上具有以下特点:

  • 不写返回值
  • 函数名称与类名相同
#include <iostream>

using namespace std;

class MobilePhone
{
private:
    string brand = "山寨";
    string model;
    double price;

public:
    MobilePhone()
    {
        cout << "创建了一个对象" << endl;
    }

    string get_brand()
    {
        return brand;
    }

    string get_model()
    {
        return model;
    }

    void set_model(string m)
    {
        model = m;
    }

    void set_price(double p)
    {
        price = p;
    }
};

int main()
{
    MobilePhone mp1; // 调用构造函数
    mp1.set_model("16Pro Max");
    mp1.set_price(9999);
    cout << mp1.get_brand() << endl;
    cout << mp1.get_model() << endl;

    MobilePhone* mp2 = new MobilePhone; // 调用构造函数
    mp2->set_model("15");
    mp2->set_price(4999);
    cout << mp2->get_brand() << endl;
    cout << mp2->get_model() << endl;
    delete mp2;

    return 0;
}

构造函数可以重载,如果程序员手动编写任意一个构造函数,编译器都不再添加构造函数。

构造函数除了创建对象外,通常还用于给成员变量初始化。

构造函数本质上是一种特殊的成员函,因此除了重载之外,也支持参数默认值和哑元等功能。

#include <iostream>

using namespace std;

class MobilePhone
{
private:
    string brand = "山寨";
    string model;
    double price;

public:
    MobilePhone(string b,string m,double p)
    {
        // 属性初始化
        brand = b;
        model = m;
        price = p;
        cout << "创建了一个对象" << endl;
    }

    string get_brand()
    {
        return brand;
    }

    string get_model()
    {
        return model;
    }

    void set_model(string m)
    {
        model = m;
    }

    void set_price(double p)
    {
        price = p;
    }
};

int main()
{
    MobilePhone mp1("Apple","16 Pro Max",9999); // 调用构造函数
    cout << mp1.get_brand() << endl;
    cout << mp1.get_model() << endl;

    MobilePhone* mp2 = new MobilePhone("小米","15",4999); // 调用构造函数
    cout << mp2->get_brand() << endl;
    cout << mp2->get_model() << endl;
    delete mp2;

    return 0;
}

3.2 构造初始化列表

构造初始化列表在现阶段可以认为是一种简便的写法,自行决定是否使用。

#include <iostream>

using namespace std;

class MobilePhone
{
private:
    string brand = "山寨";
    string model;
    double price;

public:
    MobilePhone(string b,string m,double p)
        :brand(b),model(m),price{p} // 构造初始化列表
    {
        cout << "创建了一个对象" << endl;
    }

    string get_brand()
    {
        return brand;
    }

    string get_model()
    {
        return model;
    }

    void set_model(string m)
    {
        model = m;
    }

    void set_price(double p)
    {
        price = p;
    }
};

int main()
{
    MobilePhone mp1("Apple","16 Pro Max",9999); // 调用构造函数
    cout << mp1.get_brand() << endl;
    cout << mp1.get_model() << endl;

    MobilePhone* mp2 = new MobilePhone("小米","15",4999); // 调用构造函数
    cout << mp2->get_brand() << endl;
    cout << mp2->get_model() << endl;
    delete mp2;

    return 0;
}

3.3 拷贝构造函数

3.3.1 概念

如果程序员不写拷贝构造函数,编译器也会自动添加一个重载的构造函数:拷贝构造函数。

拷贝构造函数实现的功能是,以一个已经存在的对象为基础,新创建的对象与已经存在的对象数据相同,由于封装特性,这两个对象各自是一个整体,数据是两份的。

#include <iostream>

using namespace std;

class MobilePhone
{
private:
    string brand;
    string model;
    double price;

public:
    MobilePhone(string b,string m,double p)
        :brand(b),model(m),price{p}{}

    // 如果不手写拷贝构造函数,编译器默认添加下面的拷贝构造函数
    MobilePhone(const MobilePhone& mp)
    {
        brand = mp.brand;
        model = mp.model;
        price = mp.price;
    }

    void show()
    {
        cout << brand << " " << model << " " << price << endl;
        cout << &brand << " " << &model << " " << &price << endl;
    }
};

int main()
{
    MobilePhone mp1("Apple","16 Pro Max",9999);
    // 调用拷贝构造函数
    // 新创建的mp2数据与mp1相同
    MobilePhone mp2(mp1);
    mp1.show();
    mp2.show();

    return 0;
}

【思考】默认的拷贝构造函数,可能出现什么问题?

当成员变量出现指针时,会破坏面向对象特性,此现象被称为浅拷贝。

3.3.2 浅拷贝

下面的代码中,不通过setter就能修改隐藏的成员变量,这种设计不符合面向对象的封装特性。

#include <iostream>
#include <string.h>

using namespace std;

class MobilePhone
{
private:
    char* brand;
    string model;
    double price;

public:
    MobilePhone(char* b,string m,double p)
        :brand(b),model(m),price{p}{}

    void show()
    {
        cout << brand << " " << model << " " << price << endl;
    }
};

int main()
{
    char c[20]  = "Apple";

    MobilePhone mp1(c,"16 Pro Max",9999);
    MobilePhone mp2(mp1);

    strcpy(c,"SAMSUNG");

    mp1.show();
    mp2.show();

    return 0;
}

3.3.3 深拷贝

解决浅拷贝的问题,方法就是在每个成员变量指针赋值处,单独在堆区开辟一块区域,使当前创建的对象独占。

#include <iostream>
#include <string.h>

using namespace std;

class MobilePhone
{
private:
    char* brand;
    string model;
    double price;

public:
    MobilePhone(char* b,string m,double p)
        :brand(new char[20]) // 开辟一个区域单独指向
        ,model(m),price{p}
    {
        strcpy(brand,b);
    }

    MobilePhone(const MobilePhone& mp)
    {
        brand = new char[20]; // 开辟一个区域单独指向
        strcpy(brand,mp.brand);
        model = mp.model;
        price = mp.price;
    }


    void show()
    {
        cout << brand << " " << model << " " << price << endl;
    }
};

int main()
{
    char c[20]  = "Apple";

    MobilePhone mp1(c,"16 Pro Max",9999);
    MobilePhone mp2(mp1);

    strcpy(c,"SAMSUNG");

    mp1.show();
    mp2.show();

    return 0;
}

【思考】上面的代码有什么问题?

new开辟堆内存没有delete,出现了内存泄漏的情况。

3.4 构造函数的几种调用方式

常见的几种创建对象的方式:

#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    Student(string name):name(name){}

    string get_name()
    {
        return name;
    }
};


int main()
{
    Student s1("张三");
    cout << s1.get_name() << endl;

    Student* s2 = new Student("李四");
    cout << s2->get_name() << endl;
    delete s2;

    Student s3 = Student("王五");
    cout << s3.get_name() << endl;

    string name = "赵六";
    Student s4 = name;
    cout << s4.get_name() << endl;

    return 0;
}

可以在构造函数前增加explicit关键字屏蔽赋值过程中,编译器帮忙调用构造的优化。

#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    // 明确
    explicit Student(string name):name(name){}

    string get_name()
    {
        return name;
    }
};


int main()
{
    string name = "赵六";
    // string → Student
    // 编译器帮忙调用构造函数,传递name作为构造函数的参数
    Student s4 = name;
    cout << s4.get_name() << endl;

    return 0;
}

4. 析构函数 destructor

析构函数也是一种特殊的成员函数,析构函数是与构造函数对立的函数:

构造函数

析构函数

创建对象时调用

对象销毁时被调用

可有参数,能被重载和设置参数默认值

没有参数,不能重载和设置默认值

函数名为 类名

函数名为 ~类名

通常用于对象属性初始化

通常用于释放并关闭对象资源

#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    Student(string name):name(name){}

    string get_name()
    {
        return name;
    }

    // 如果程序员不写,也会添加一个函数体为空的析构函数,如下
    //    ~Student(){}

    ~Student()
    {
        cout << name << "销毁了" << endl;
    }
};


int main()
{
    Student s1("张三");
    Student* s2 = new Student("李四");
    delete s2; // s2触发析构函数

    cout << "主函数结束" << endl;
    return 0;
} // s1触发析构函数

回到3.3.3深拷贝的代码中,可以通过析构函数释放new出来的堆内存。

#include <iostream>
#include <string.h>

using namespace std;

class MobilePhone
{
private:
    char* brand;
    string model;
    double price;

public:
    MobilePhone(char* b,string m,double p)
        :brand(new char[20]) // 开辟一个区域单独指向
        ,model(m),price{p}
    {
        strcpy(brand,b);
    }

    MobilePhone(const MobilePhone& mp)
    {
        brand = new char[20]; // 开辟一个区域单独指向
        strcpy(brand,mp.brand);
        model = mp.model;
        price = mp.price;
    }

    // 析构函数
    ~MobilePhone()
    {
        cout << "释放资源:" << brand << endl;
        // 释放堆内存资源
        delete brand;
    }

    void show()
    {
        cout << brand << " " << model << " " << price << endl;
    }
};

int main()
{
    char c[20]  = "Apple";

    MobilePhone mp1(c,"16 Pro Max",9999);
    MobilePhone mp2(mp1);

    strcpy(c,"SAMSUNG");

    mp1.show();
    mp2.show();

    return 0;
}

5. 作用域限定符 ::

5.1 名字空间 namespace

本次学习几乎所有内置的功能都属于C++标准库,放置在std名字空间下,例如字符串类完整的名称应该是std::string

名字空间是用来解决不同的作用域下的重名问题。

#include <iostream>

using namespace std; // 默认使用std::

int a = 1;

// 自定义名字空间
namespace my_space
{
    int a = 3;
    int b = 4;
}

using namespace my_space;

int main()
{
    int a = 2;
    // 手动使用std::
    std::string str = "今天才周二";
    std::cout << str << std::endl;

    cout << a << endl; // 2
    // “匿名名字空间”
    cout << ::a << endl; // 1
    cout << my_space::a << endl; // 3
    cout << b << endl; // 4

    return 0;
}

5.2 类内声明,类外定义

之前类中的成员函数都是在类内直接定义的,在实际开发过程中通常需要声明与定义分离,在类内只声明,函数定义放置到类外。

#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    // 类内声明
    Student(string n);
    string get_name();
    void set_name(string n);
    ~Student();
};

// 函数名前加 类名::
Student::Student(string n):name(n){}

string Student::get_name()
{
    return name;
}

void Student::set_name(string n)
{
    name = n;
}

Student::~Student()
{
    cout << "析构函数" << endl;
}

int main()
{
    Student s("张三");
    cout << s.get_name() << endl;
    s.set_name("张三丰");
    cout << s.get_name() << endl;

    return 0;
}

5.3 调用静态成员

作用域限定符可以应用在静态成员中,后续讲解。

6. this指针

6.1 概念

this指针是类内的一种特殊指针,存在于成员函数中,存储了当前类对象的首地址

#include <iostream>

using namespace std;

class Dog
{
public:
    void test()
    {
        // 可以通过this找到当前类外部对象
        // 突破作用域
        cout << this << endl;
    }
};

int main()
{
    Dog d1;
    cout << &d1 << endl; // 0x61fe8b
    d1.test(); // 0x61fe8b

    Dog* d2 = new Dog;
    cout << d2 << endl;
    d2->test(); // 0x1001108
    delete d2; // 0x1001108

    return 0;
}

6.2 类内调用其他成员

成员必须由对象调用,类内成员函数调用其他成员时,编译器自动添加this找到调用的对象。

#include <iostream>

using namespace std;

class Dog
{
private:
    string name;

public:
    Dog(string n)
    {
        // 编译器通过this指针找到对象,调用成员
        this->name = n;
    }

    string get_name()
    {
        return this->name;
    }

    void test()
    {
        cout << this->get_name() << endl;
    }
};

int main()
{
    Dog d("大黄");
    d.test(); // 大黄

    return 0;
}

6.3 成员变量与局部变量重名

#include <iostream>

using namespace std;

class Dog
{
private:
    string name;

public:
    Dog(string name):name(name) // 构造初始化列表也可以区分重名
    {

    }

    void set_name(string name)
    {
        // 对象调用成员,与局部进行区分
        this->name = name;
    }

    string get_name()
    {
        return name;
    }
};

int main()
{
    Dog d("小白");
    cout << d.get_name() << endl;
    d.set_name("小黄");
    cout << d.get_name() << endl;

    return 0;
}

6.4 链式调用

如果一个成员函数的返回值是当前类型的引用,则表示此函数return *this,支持链式调用。

#include <iostream>

using namespace std;

class Value
{
private:
    int val;

public:
    Value(int val):val(val){}

    int get_val()
    {
        return val;
    }

    Value& add(int i)
    {
        cout << &val << endl;
        val += i;
        return *this;
    }
};

int main()
{
    // 普通调用
    Value v1(1);
    v1.add(2);
    v1.add(3);
    v1.add(4);
    cout << v1.get_val() << endl; // 10

    Value v2(1);
    // 链式调用
    cout << v2.add(2).add(3).add(4).get_val() << endl; // 10
    cout << v2.get_val() << endl; // 10

    return 0;
}

7. static关键字

7.1 静态局部变量

使用static修饰局部变量,这样的变量就是静态局部变量。

静态局部变量所在的函数第一次被调用时,静态局部变量被创建,函数执行完成后,此变量不会被销毁,下次调用此函数时,继续使用已经创建的静态局部变量,直到程序运行结束销毁

#include <iostream>

using namespace std;

class Test
{
public:
    void test_static()
    {
        int a = 1;
        static int b = 1;
        cout << ++a << endl;
        cout << ++b << endl;
        cout << endl;
    }
};

int main()
{
    Test t1;
    t1.test_static(); // b创建
    t1.test_static();
    Test t2;
    t2.test_static();
    t2.test_static();

    return 0;
} // b销毁

7.2 静态成员变量

使用static修饰成员变量,就是静态成员变量。

非const的静态成员变量需要类内声明,类外初始化;一个类的所有对象共用一份静态成员变量。

静态成员变量可以脱离对象使用,因此:

  • 程序启动时开辟内存
  • 对象销毁后,不销毁静态成员变量
  • 程序结束时销毁

建议直接使用 类名:: 的方式调用静态成员变量,因为可以增加代码的可读性。

#include <iostream>

using namespace std;

class Test
{
public:
    int a = 1;
    //    static int b = 1; 错误:非const的静态成员变量不能类内初始化
    static int b; // 只声明
};

// 类外初始化
int Test::b = 1;

int main()
{
    cout << Test::b << " " << &Test::b << endl; // 1 0x403004

    // 局部代码块
    {
        Test t1;
        cout << ++t1.a << " " << &t1.a <<endl; // 2 0x61fe8c
        cout << ++t1.b << " " << &t1.b<< endl; // 2 0x403004

        Test t2;
        cout << ++t2.a <<  " " << &t2.a << endl; // 2 0x61fe88
        cout << ++t2.b <<  " " << &t2.b << endl; // 3 0x403004
    } // t1、t2销毁

    cout << Test::b << " " << &Test::b << endl; // 3 0x403004

    return 0;
} // Test::b销毁

7.3 静态成员函数

使用static修饰成员函数,就是静态成员函数。

静态成员函数也可以脱离对象,直接使用类名调用,推荐使用这种方式调用。因此静态成员函数没有this指针不能在静态成员函数中调用其他非静态成员,但是可以调用其他静态成员。

#include <iostream>

using namespace std;

class Demo
{
public:
    int a = 1;
    void test()
    {
        cout << "成员函数" << endl;
    }

    static int b;

    static void func(int)
    {
        cout << "静态成员函数2" << endl;
    }

    static void func()
    {
        cout << "静态成员函数" << endl;
//        cout << this << endl; 错误
//        cout << a << endl; 错误
//        test(); 错误
        cout << b << endl;
        func(2);
    }
};

int Demo::b = 1;

int main()
{
    Demo::func(); // 推荐
    Demo d;
    d.func();
}

通常一些偏向于功能性接口(简单且直接)的设计考虑采用静态成员函数。

7.4 static的应用举例——单例模式

设计模式是一套被反复使用、多数人知晓的、经过分类的代码设计经验的总结。理论上讲,任何一个面向对象的编程语言都可以学习设计模式。

单例模式是设计模式中最简单的一种,本节通过static的性质,分别使用指针和引用编写两个简化版的单例模式案例说明static的应用。

单例模式设计的类可以确保在使用的过程中始终存在一个对象。

基于指针的单例模式:

#include <iostream>

using namespace std;

class Singleton
{
private:
    Singleton(){}
    Singleton(const Singleton&){}
    static Singleton* instance;

public:
    // 脱离对象调用的静态成员函数:目的是获得对象
    static Singleton* get_instance()
    {
        if(instance == NULL)
            instance = new Singleton;
        return instance;
    }
};

Singleton* Singleton::instance = NULL;

int main()
{
    cout << Singleton::get_instance() << endl;
    cout << Singleton::get_instance() << endl;
}

基于引用的单例模式:

#include <iostream>

using namespace std;

class Singleton
{
private:
    Singleton(){}
    Singleton(const Singleton&){}

public:
    // 脱离对象调用的静态成员函数:目的是获得对象
    static Singleton& get_instance()
    {
        static Singleton instance;
        return instance;
    }
};

int main()
{
    cout << &Singleton::get_instance() << endl;
    cout << &Singleton::get_instance() << endl;
}

8. const关键字

8.1 修饰成员函数

const修饰的成员函数,表示常成员函数。

常成员函数可以调用非const的成员变量,但是不能修改数值;不能调用非const的成员函数。

#include <iostream>

using namespace std;

class Computer
{
private:
    string brand = "联想";

public:
    void set_brand(string brand)
    {
        this->brand = brand;
    }

    string get_brand() const
    {
        return brand;
    }

    void test() const
    {
        cout << brand << endl;
//        brand = "惠普"; 错误
//        set_brand("戴尔"); 错误
        cout << get_brand() << endl;
    }
};

int main()
{
    Computer c;
    c.test();
}

尽量把成员函数设置为常成员函数,使代码更加严谨,例如getter函数。

8.2 修饰对象

const修饰对象,表示该对象为常量对象,这样的对象不能修改成员变量的数值,也不能调用非const的成员函数。

#include <iostream>

using namespace std;

class Computer
{
private:
    string brand;

public:
    string system = "Win11";

    Computer(string brand):brand(brand){}

    void set_brand(string brand)
    {
        this->brand = brand;
    }

    string get_brand() const
    {
        return brand;
    }
};

int main()
{
    // 常量对象
    const Computer c1("惠普");
    cout << c1.system << endl;
//    c1.system = "Win12"; 错误
//    c1.set_brand("苹果"); 错误
    cout << c1.get_brand() << endl;

    Computer const c2("戴尔");
    cout << c2.system << endl;
//    c2.system = "Win95"; 错误
//    c2.set_brand("华硕"); 错误
    cout << c2.get_brand() << endl;
}

8.3 修饰成员变量

const修饰的成员变量是常成员变量,表示改成员变量的值不可变。

常成员变量的初始值可以通过两种方式设定:

  • 直接初始化
  • 构造初始化列表

上述两者同时使用时,以后者为准。

#include <iostream>

using namespace std;

class Pig
{
private:
    const string variety = "黑猪"; // 品种
    const int weight;

public:
    Pig(string v,int w):variety(v),weight(w){}\

    Pig(int w):weight(w){}

    void test()
    {
//        variety = "荷兰猪"; 错误
//        weight = -1; 错误
        cout << variety << endl;
        cout << weight << endl;
    }
};

int main()
{
    Pig p("GG Bond",100);
    p.test();

    Pig p2(90);
    p2.test();
}

8.4 修饰引用参数

详见第一天的引用参数章节。

引用参数在应该能被添加为const的情况下,尽量添加const修饰,以达到引用参数的安全性。

8.5 constexpr关键字-C++11

传统的const没有编译和运行的概念,而constexpr是一种在编译时确定的表达式。也就是说,编译器看到constexpr就可以进行优化了。

#include <iostream>

using namespace std;

class Test
{
public:
    int a = 1;
    const int b = 2;
//    constexpr int c = 3; 错误
    const static int d = 4;
    constexpr static int e = 5;
};

int main()
{
    Test t;
    cout << t.a << endl;
    cout << t.b << endl;
    cout << t.d << endl;
    cout << t.e << endl;
}

上面代码中,非静态的变量abc要跟对象绑定,对象的创建严格的讲是在运行时发生的,因此上面的变量c在编译时无法确定,这与constexpr的含义冲突,编译出错。

#include <iostream>
#include <array>

using namespace std;

constexpr int func(int i)
{
    return i+1;
}

int main()
{
    // 创建一个长度为3的int数组
    // <>的第二个参数(本例中的3)必须在编译时确定
    array<int,3> arr;

    int i = 4;
    cout << func(i) << endl; // 5

    array<int,func(2)> arr2;
//    array<int,func(i)> arr2; 错误
}

在实际的使用过程中,constexpr的受限较多,请根据实际情况决定是否使用。


原文地址:https://blog.csdn.net/Xiaomo1536/article/details/143806377

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!