• C++中const 的各种用法


    C++中const 关键字的用法

    const修饰变量

    const 主要用于把一个对象转换成一个常量,例如:

    const int size = 512;
    size = 0;        // error: assignment of read-only variable

    上面的例子中,定义size为常量并初始化为512,变量size仍是一个左值,但是现在这个左值是不可修改的,任何修改size的尝试都会导致编译错误。

    因为常量在定以后就不能被修改,因此const对象定义时必须初始化,否则会引起编译错误,例如:

    const int size;  // error: uninitialized const

    在全局作用域里定义非const变量时,它在整个程序中都可以访问。我们可以把一个非const变量定义在一个文件中,假设已经做了合适的声明,就可以在另外的文件中使用这个变量:

    // file1.cpp
    int counter;    // definition
    
    // file2.cpp
    extern int counter; // use counter from file1
    ++counter;            // increments counter defined in file1

    在全局作用域里声明的const变量是定义该对象的文件的局部变量,此变量只存在于那个文件中,不能被其它文件访问。

    通常头文件中不能存放变量定义,但const变量是一个例外,当我们在头文件中定义了const变量后,每个包含该头文件的源文件都有了自己的const变量,其名称和值都一样。

    通过指定const变量为extern,就可以在整个程序中访问const对象,例如:

    // file1.cpp
    extern const int counter = 0;    // definition
    
    // file2.cpp
    extern const int counter;      // use counter from file1
    

    总结:

    1、非const变量默认为extern。

    2、要使const变量能够在其它的文件中访问,必须显式地指定它为extern。


    const修饰引用

    引用(reference)就是对象的另一个名字,在实际应用中,引用主要用作函数的形参。

    引用必须用与该引用同类型的对象初始化:

    int i = 42; 
    int &r1 = i;     // ok 
    int &r2;        // error: a reference must be initialized
    int &r3 = 10;   // error: initialize must be an object

    当引用初始化后,不能将其再绑定到另一个对象上:

    int i = 42; 
    int j = 37;
    int &r1 = i;     // ok 
    r1 = j;     // i=37

    const引用是指向const对象的引用,

    const int i = 42;
    const int &r1 = i;     // ok 
    int &r2 = i;     // error: nonconst reference to a const object

    上面的例子中,可以读取但不能修改r1,因此,任何对r1的赋值都是不合法的。

    同理,用i初始化r2也是不合法的,r2是普通的非const引用,不能指向const对象。

    const 引用可以初始化为不同类型的对象或者初始化为右值,如字面值常量:

    int i = 42; 
    const int &r1 = i;
    const int &r2 = 42; 
    const int &r3 = r2 + i; // 84

    const引用绑定到不同但相关类型的对象上是合法的,如下:

    double i = 42.1;
    const int &r1 = i;     // ok, r1=42
    int &r2 = i;     // error 

    这是因为const引用是只读的,编译器会自动创建一个临时变量,然后将const引用绑定到这个临时变量上:

    int temp = i;
    const int &r1 = temp;

    总结:

    1、普通非const引用只能绑定到与该引用同类型的对象;

    2、const引用则可以绑定到不同但相关的类型的对象,或绑定到右值;


    const修饰指针

    1、const指针

    前面提到,const对象在定义的同时必须初始化,const指针也遵循这一规则。

    作为常量,const指针的值不能被修改,这就意味着const指针初始化以后不能再指向其它对象,任何试图给const指针赋值的行为都会导致编译错误。

    int i = 42;
    int j = 42;
    int* const p1 = &i;     // ok 
    int* const p2;     // error, uninitialzed const
    p1 = &j;     // error, assignment of read-only variable

    const指针本身虽不能修改,但却可以通过它修改所指向的对象的值,

    int i = 42;
    int* const p1 = &i;     // ok 
    *p1 = 37;       // i=37

    2、指向const对象的指针

    如果指针指向const对象,则不允许用指针来改变其所指的const值,为了保证这个特性,C++强制要求指向const对象的指针必须具有const特性:

    const int i = 42;
    int* p1 = &i;           // error 
    int* const p2 = &i;     // error 
    const int* p3 = &i;     // ok 

    指向const对象的指针本身并不是const的,因此定义时可以不必进行初始化,且可以再指向其它对象;

    允许把非const对象的地址赋给指向const对象的指针。

    const int i = 42;
    int j = 42; 
    const int* p1;     // ok 
    p1 = &i;        // ok
    p1 = &j;        // ok
    *p1 = 37;       // error

    但不允许指向const对象的指针修改其所指向的值,如上例,p1指向非const变量j,但仍不允许通过p1修改变量j。

    3、指向const对象的const指针

    int j = 42;
    const int* const p1 = &j; 

    这里,既不能修改p1所指向的对象的值,也不允许修改该指针的指向(即p1中存放的地址值)。

    4、const和typedef

    typedef int *pint;
    pint const pt;        // 该语句相当于int *const pt,
    const pint pt;      //该语句还是相当于int *const pt,而非const int *pt

    如上面的例子,把const放在类型pint之前,容易引起对所定义的真正类的误解,要特别注意。

    总结:

    1、const指针,const修饰的是指针;

    2、指向const对象的指针,const修饰的是指针所指向对象的类型;


    const修饰迭代器

    标准库为每一种标准容器(如vector)定义了一种迭代器类型,迭代器是一种检查容器内元素并遍历元素的数据类型。

    例如:

    vector<int>::iterator iter = vec.begin();
    
    for (; iter!=vec.end(); ++iter) {
        cout<< *iter << endl;
    }

    1、const迭代器

    声明一个const迭代器时,必须初始化迭代器,一旦被初始化后,就不能改变它的值,但可以改变它所指向的元素的值:

    vector<int> vec(10);
    const vector<int>::iterator iter1 = vec.begin();
    *iter1 = 1; // ok
    iter1++;    // error

    2、指向const的迭代器(const_iterator

    每种容器类型还定义了一种名为const_iterator的类型,该类型只能用于读取容器内元素,但不能改变其值。

    对const_iterator类型解引用时,返回的是一个const值,不允许用const_iterator进行赋值,例如:

    const vector<int>::iterator iter1 = vec.begin();
    vector<int>::const_iterator iter2 = vec.begin();
    
    *iter1 += 10;   // ok
    *iter2 += 10;   // error

    对const_iterator迭代器,它自身的值可以改变(即指向不同的元素),但不能用来改变其所指向的元素的值。

    const_iterator迭代器可以用来指向一个const的vector对象,而const迭代器对象不被允许,因为它可能改变所指向元素的值,而这个元素是只读的。

    const vector<int> vec(10);
    const vector<int>::iterator iter1 = vec.begin();    // error
    vector<int>::const_iterator iter2 = vec.begin();    // ok

    const修饰类

    1、const数据成员

    构造函数分为两个阶段执行:a) 初始化阶段,在初始化列表中完成;b) 普通的计算阶段,在构造函数函数体中完成。

    在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化:对于类类型数据成员,运行其默认构造函数完成初始化;内置或者复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0.

    没有默认构造函数的类类型成员,以及const或引用类型的成员,必须在构造函数初始化列表中进行初始化。

    class T { 
        public:
            T(int k); 
    
        private:
            int i;
            const int ci; 
            int &ri;
    };
    
    
    // no explicit constructor initializer
    T::T(int k)
    {
        i = k;  // ok
        ci = k; // error: cannot assign to a const
        ri = i; // error: unitialized
    }
    
    // ok, explicit initialze reference and const members
    T::T(int k):i(k),ci(k),ri(i) {}

    2、const函数成员

    在类定义中,既可以定义const数据成员,也可以定义const函数成员。

    在定义const成员函数时:

    1、const关键字必须同时出现在声明和定义中,若只出现在一处,会出现编译错误;

    2、const成员函数不能改变其所操作对象的数据成员(mutable成员除外);

    3、构造函数不能为const,创建类类型的const对象时,运行一个普通构造函数来初始化该const对象即可。

    例如:

    class Sale_item {
        public:
            double avg_price() const;
            bool same_isbn(const Sale_item &rhs) const {return isbn == rhs.isbn;}
    
        private:
            string isbn;
            unsigned units_sold;
            double revenue;
    };
    
    double Sale_item::avg_price() const
    {
        if (units_sold) 
            return revenue / units_sold;
        else
            return 0;
    }

    我们知道,对于非static成员函数,都有一个隐含的this参数,this指针与调用成员函数的对象绑定在一起:

    1、对于非const成员函数,this是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址;

    2、对于const成员函数,this是一个指向const类类型的const指针,既不能改变this所指向的值,也不能改变this所保存的地址;

    3、不能从const成员函数返回指向类对象的普通引用,const成员函数只能返回*this作为一个const引用。

    基于成员函数是否为const,可以重载一个成员函数;同样地,基于一个指针形参是否指向const,可以重载一个成员函数。

    const对象只能使用const成员,非const对象可以使用任一成员,但非const版本是一个更好的匹配。

    class Screen {
        public:
            Screen():contents("hello
    "){}                                 
    
            Screen& display(ostream &os) 
                { os<<"non const:	"<<contents; return *this;}
    
            const Screen& display(ostream &os) const 
                { os<<"const:	"<<contents; return *this;}
    
        private:
            string contents;
    };
    
    
    int main()
    {
        Screen s1; 
        const Screen s2; 
            
        s1.display(cout);   // non const version
        s2.display(cout);   // const version
    }

    3、可变数据成员(mutable)

    可变数据成员永远都不能为const,即使它是const对象的成员时也如此。因此,const成员函数可以改变mutable成员。

    class T {
        public:
            T():len(0){}
    
            void increment() const
                { len++; cout<<len<<endl;}
            
        private:
            mutable int len;
    };
    
    
    int main()
    {
        const T t;
        t.increment();  // len = 1
        t.increment();  // len = 2
    }

    如上例,const对象调用const成员函数,改变了mutable数据成员。

    4、static类成员

    static成员是类的组成部分,但不是任何对象的组成部分;

    static成员函数没有this指针,static成员函数不能声明为const,也不能声明为virtual;

    static数据成员必须在类定义体的外部定义,static成员不是通过构造函数进行初始化,而是应该在定义时进行初始化。

    const static整型数据成员在类的定义体中初始化时,该数据成员仍须在类的定义体之外进行定义。

  • 相关阅读:
    flask全栈开发3 模板
    flask全栈开发2 URL与视图
    flask全栈开发1 课程简介
    微信公众号开发中遇到的问题总结
    python web学习路线
    内存数据库Memcached和redis基本使用
    2019年8月12号成长题目
    2019年8月10号成长题目
    2019年8月7号成长题目
    SpringCloud简介与5大常用组件
  • 原文地址:https://www.cnblogs.com/chenny7/p/4418926.html
Copyright © 2020-2023  润新知