C++ Primer 学习笔记_25_类与数据抽象(11)--const 用法小结、static与const以及static const(const static)

一、const 用法总结

1、可以对const 的用法做个小总结:

const int n = 100;  //定义常量

const Test t(10);

const int & ref = n;   //const引用

int& ref = n;  //Error

【const与指针】

const int* p; //const出现在*前面,表示*p是常量 (*p = 200; //Error)

int * const p2;  //const出现在*后面,表示p2是常量 (p2 = &n2; //Error)

const int* const p3 = &n;  //*p3是常量,p3也是常量

【类】

如果有const成员,const成员的初始化必须只能在构造函数初始化列表中进行

const修饰成员函数,表示该成员函数不能修改对象状态,也就时说它只能访问数据成员,但是不能修改数据成员

【例子】

#include <iostream>
using namespace std;

int main()
{
        const int n = 100;
        int n2 = 200;
        int n3 = 300;
        const int* p = &n;
        *p = 200;
        int* const p2 = &n3;
        p2 = &n2;
        const int* const p3 = &n;
        return 0;
}

运行结果:

test.cpp:11:7: 错误: 向只读位置‘* p’赋值

test.cpp:13:8: 错误: 向只读变量‘p2’赋值

2、const一些常见的问题

(1)由于const常量在定义后就不能修改,所以定义时必须初始化。

const int i, j = 0;  //错误,i必须初始化

(2)全局const变量与非const变量

——在全局作用域定义非const变量时,在整个程序中都可以访问。

//file1.cpp
int counter = 10;
//file2.cpp
#include <iostream>

using namespace std;

int main()
{
        extern int counter;
        for(int i = 0; i != counter; i++)
        {
                cout << i << " ";
        }
        cout << endl;
}

运行结果:

0 1 2 3 4 5 6 7 8 9

——在全局作用域定义const变量时是定义该对象的文件的局部变量,不能在整个程序中可以访问。需要在定义的时候在const前加上extern关键字,才可以在整个程序中访问。

//file1.cpp
extern const int counter = 10;
//const int counter = 10;  //这样定义在file2中不能访问到
//file2.cpp
#include <iostream>

using namespace std;

int main()
{
        extern const int counter;
        for(int i = 0; i != counter; i++)
        {
                cout << i << " ";
        }
        cout << endl;
}

运行结果:

0 1 2 3 4 5 6 7 8 9

(3)在C++中主要是用const替代#define的功能,建议在C++中,用const取代#define。优点有很多,比较突出的优点是编译器会对const进行类型安全检查,而#define没有类型安全检查,在字符替换时可能会产生意料不到的错误。

(4)指针在不同地方的含义

——double* ptr = &value;

答:ptr是一个指向double类型的指针,ptr的值可以改变,可以通过ptr改变value的值。

——const double* ptr = &value;

答:ptr是一个指向const double类型的指针,ptr的值可以改变,不能通过ptr改变value的值。

——double* const ptr = &value;

答:ptr是一个指向double类型的const指针,ptr的值不能改变,可以通过ptr改变value的值。

——const double* const ptr = &value;

答:ptr是一个指向const double类型的const指针,ptr的值不能改变,不能通过ptr改变value的值。

【一个有趣的例子】

看下面代码:

#include <iostream>
int main()
{
        char* const a[2] = {"abc", "def"};
        a[0] = "ABC";  //错误
        *(a+1);  //正确
}

解释:a[2]是一个数组,可以当作是指针*a,然后根据上述的理解,a的值可以改变,不能通过a改变数组的值。

(5)作用区的const应用

【例子1】

下面的变量p和它所指向的字符串分别存放在()

#include <iostream>
int main()
{
        char* p = "hello world";
}

A、堆和常量区   B、栈和栈    C、栈和常量区    D、栈和堆

解答:C。"hello world"存放在文字常量区,变量p存放在栈上。

——现在做些修改

#include <iostream>
int main()
{
        char* p = "hello world";
        p[0] = 'a';
}

运行结果:

导致运行时崩溃,因为程序对文字常量区的内容试图做修改,而这是不允许的。此时可以改一下p的定义:const char* p = "hello world";  这样编译器就会直接识别出错误。

(6)const修饰返回值

【一个例子】

猜猜下面程序的运行结果:

#include <iostream>
using namespace std;

char* getstr()
{
        char p[] = "hello world";
        return p;
}

int main()
{
        char *str = NULL;
        str = getstr();
        cout << str << endl;
        return 0;
}

解释:结果是乱码。p是一个数组,其内存分配在栈上,故getstr()返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原来的内容已经被清楚,新内容不可知。

——解决方案:由于栈容量小,每次作用域结束就会清除;因此可以不存在栈区,存在静态区(全局区)或者是堆区。比如下述,加上static,存到静态数据区。

#include <iostream>
using namespace std;

char* getstr()
{
        static char p[] = "hello world";
        return p;
}

int main()
{
        char *str = NULL;
        str = getstr();
        cout << str << endl;
        return 0;
}

运行结果:

hello world

(7)const用来修饰函数参数

——【例子1】

#include <iostream>
void fun(int* i){ }
int main()
{
        const int a = 1;
        fun(&a);
        return 0;
}

运行结果:编译出错

解释:在函数中加const则可以运行,void fun(const int* i){ }

——【例子2】

#include <iostream>
void fun(int& i){ }
int main()
{
        fun(1);
        return 0;
}

运行结果:编译出错

解释:在函数中加const则可以运行,void fun(const int& i){ }

——【例子3】

下面哪些选项能编译通过()(不定项)

int i;

char a[10];

string f();

string g(string &str);

A、if(!!I) {f();}      B、g(f());      C、a = a + 1;       D、g("ABC");

答案:A。

B、关键点时g++下临时变量都作为常量故f()返回值作为临时变量(常量)传递给函数g时出错,因为常量不能传递给一个非常量引用。详细如下:

#include <iostream>
#include <string>
using namespace std;  //string的时候需要声明,忘了
string f() { return "a"; }   //此时,只要修改为string g(const string &str) { },即在参数前加上const修饰就可以运行。
//string g(string &str) { } //不能运行
string g(const string &str) { }
int main()
{
        g(f());
        return 0;
}

C、a是常量,不可以进行赋值

D、同B。

3、static与const以及static const(const static)

(1)说明在C++中static、const以及static const成员变量的初始化?

解答:

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

在 C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。const数据成员只在某个对象生存期是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值时什么。(好像在C++11中可以在定义处初始化)

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

class Test
{
public:
        Test(): a(0) { }
        enum { size1 = 100, size2 = 200};
private:
        const int a;
        static int b;
        const static int c; //与static const int c;相同,考虑c为整型时,有三种情况:第一,可在此处声明,但仍需在类定义体外进行定义初始化,因为const必须初始化。第二,直接在此处进行定义初始化,就不能在类定义体外进行赋值,const不能重复赋值。第三,绝对不允许在构造函数的初始化列表进行初始化。注意:c为非整型时,不能在此处初始化,整型包括char、short、long、int、float、double(但是string不行)
};
int Test::b = 0;
const int Test::c = 0;

【例子】

下面的()中可以用哪些选项填充,运行结果是:0  20  1  20?

#include <iostream>
using namespace std;

class Test
{
public:
        () int a;
        () int b;
public:
        Test(int _a, int _b) : a(_a)
        {
                b = _b;
        }
};
int Test::b;

int main()
{
        Test t1(0, 0), t2(1, 1);
        t1.b = 10;
        t2.b = 20;
        cout << t1.a <<" " << t1.b << " " << t2.a << " " << t2.b << endl;
}

A、statc/const      B、const/static      C、-/static      D、const static/static          E、None of the above

答案:BC。发现t1与t2的b是相同的,仿佛“只有一份”,所以是静态的。相反,a肯定是非静态的。所以,a只是在初始化列表中初始化,const和非const都满足,因此答案是BC。

参考:

C++ primer 第四版

Effective C++ 3rd

http://blog.csdn.net/jnu_simba/article/details/9282235

http://blog.csdn.net/zjf280441589/article/details/24704603

C++编程规范

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 01:42:51

C++ Primer 学习笔记_25_类与数据抽象(11)--const 用法小结、static与const以及static const(const static)的相关文章

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++ Primer 学习笔记_56_类与数据抽象 --消息处理示例

复制控制 --消息处理示例 说明: 有些类为了做一些工作需要对复制进行控制.为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中.Message上有 save和 remove操作,用于在指定Folder中保存或删除该消息. 数据结构: 对每个Message,我们并不是在每个Folder中都存放一个副本,而是使每个Message保存一个指针集(set),set中

C++ Primer 学习笔记_57_类与数据抽象 --管理指针成员

复制控制 --管理指针成员 引言: 包含指针的类需要特别注意复制控制,原因是复制指针时只是复制了指针中的地址,而不会复制指针指向的对象! 将一个指针复制到另一个指针时,两个指针指向同一对象.当两个指针指向同一对象时,可能使用任一指针改变基础对象.类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在.指针成员默认具有与指针对象同样的行为. 大多数C++类采用以下三种方法之一管理指针成员: 1)指针成员采取常规指针型行为:这样的类具有指针的所有缺陷但无需特殊的复制控制! 2)类

C++ Primer 学习笔记_55_类与数据抽象 --析构函数

复制控制 --析构函数 引言: 在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源.析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充. 1.何时调用析构函数 撤销类对象时会自动调用析构函数: Sales_item *p = new Sales_item; { Sales_item item(*p); //调用复制构造函数 delete p; //调用指针p的析构函数 } //调用对象item的析构函数 动态分配的对象只有在指向该对象的指针被删除时才撤销,

C++ Primer 学习笔记_53_类与数据抽象 --友元、static成员

类 --友元.static成员 一.友元 友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类(对未被授权的函数或类,则阻止其访问):友元的声明以关键字friend开始,但是它只能出现在类定义的内部.友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的访问控制影响. [最佳实践] 通常,将友元声明成组的放在类定义的开始或结尾是个好主意! 1.友元关系:一个例子 假设一个窗口管理类Window_Mgr可能需要访问由其管理的Screen对象的内部

C++ Primer 学习笔记_54_类与数据抽象 --复制构造函数、赋值操作符

复制控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,需要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数:具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数:当将该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式使用复制构造函数.     析构函数:作为构造函数的互补,当对象超出作用域或动态分配的对象被删除

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式.auto_ptr与单例模式.const成员函数.const 对象.mutable修饰符 前言 [例]写出面向对象的五个基本原则? 解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则 里氏替换原则:子类型必须能够替换他们的基类型. 设计模式分为三种类型:创建型模式.结构型模式和行为型模式 一.static 与单例模式 1.单例模式 单例模式的意图:保证一个类仅有一个实例,并提供一个访问它

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针 1.引言 在前面提到过,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针.这个隐含形参命名为this. 2.返回*this 成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享.成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以

C++ Primer 学习笔记_17_类与数据抽象(3)_类作用域

C++ Primer 学习笔记_17_类与数据抽象(3)_类作用域 引言: 每个类都定义了自己的新作用域与唯一的类型.即使两个类具有完全相同的成员列表,它们也是不同的类型.每个类的成员不同与任何其他类(或任何其他作用域)的成员. 一.类作用域中的名字查找 1)首先,在使用该名字的块中查找名字的声明.只考虑在该项使用之前声明的名字. 2)如果在1)中找不到,则在包围的作用域中查找. 如果找不到任何声明,则出错. 类的定义实际上是在两个阶段中处理: 1)首先,编译器声明: 2)只有在所有成员出现之后