文章目录
一、const
1. const修饰基本数据类型
1.1 用const修饰基本数据类型
1.1.1 const修饰一般常量及数组
- 对于这些基本的数据类型,
修饰符const可以用在类型说明符前,也可以用在类型说明符后,其结果是一样的
。
int const a = 100;
const int a = 100; //与上面等价
int const arr [3] = {1,2,3};
const int arr [3] = {1,2,3};//与上面等价
1.1.2 const修饰指针(*)
以下的区别是指针能否指向别的变量,指向的数据能否被改变
char *p = "hello"; // 非const指针,
// 非const数据
const char *p = "hello"; // 非const指针,
// const数据
char * const p = "hello"; // const指针,
// 非const数据
const char * const p = "hello"; // const指针,
// const数据
常量指针
当为常量指针时,不可以通过修改所指向的变量的值,但是指针可以指向别的变量。
int a = 5;
const int *p =&a;
*p = 20; //error 不可以通过修改所指向的变量的值
int b =20;
p = &b; //right 指针可以指向别的变量
指向常量的指针(指针常量)
当为指针常量时,指针常量的值不可以修改,就是不能指向别的变量,但是可以通过指针修改它所指向的变量的值。
int a = 5;
int *const p = &a;
*p = 20; //right 可以修改所指向变量的值
int b = 10;
p = &b; //error 不可以指向别的变量
上述的常量指针和指向常量的指针可以总结为:
左定值,右定向,const修饰不变量
(左和右相对于*)
以下对比无const修饰和都有const修饰情况
int* p1 = &a;
*p1 = 100; //可以修改值
p1 = &b; //也可以修改方向
------------------------------------------------------------
const int* const p5 = &a;//指针前有指针后也有 均不可以改变
2. const修饰函数
const更大的魅力是它可以修饰函数的参数、返回值,甚至函数的定义体
2.1 用const修饰函数的参数
const只能修饰输入参数,不能修改输出参数
-
如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针指向的内容,起到保护作用,【适用:左定值】
-
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const修饰。【enum类型的数据不用引用】
-
对于非内部数据类型的参数而言,
应该将“值传递”的方式改为“const引用传递”
,例如结构体
- 例如:
void Func(A a)
这样声明的函数注定效率比较底。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间,浪费内存。而改为“引用传递”,”仅借用一下参数的别名而已,不需要产生临时对象。但“引用传递”有可能改变参数a,加const修饰即可。值得注意的是,是否应将void Func(int x)
改写为void Func(const int &x)
,以提高效率?完全没有必要,既达不到提高效率的目的,又降低了函数的可理解性。因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当
。 - 传递一个指向对象的指针与引用方式效果相同,但是引用比指针更加好用,可以阅读博文C++ 引用详解(引用的特点,引用与指针的区别,引用的其他使用)了解引用的特点
- 例如:
2.2 用const修饰函数的返回值
- 如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。
const char *str = GetString();
- 如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
即如果返回值是结构体,加const则有意义,如果返回是基本数据类型,加const返回则没有意义
例如不要把函数int GetInt(void) 写成const int GetInt(void)。
同理不要把函数A GetA(void) 写成const A GetA(void),其中A为用户自定义的数据类型。
如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A & GetA(void)的确能提高效率。
但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,
否则程序会出错。
- 函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
class A
{…
A & operate = (const A &other); // 赋值函数
};
A a, b, c; // a, b, c 为A的对象…
a = b = c; // 正常的链式赋值
(a = b) = c; // 不正常的链式赋值,但合法
如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。
上例中,语句 a = b = c仍然正确,但是语句 (a = b) = c 则是非法的。
2.3 用const修饰成员函数的定义
-
任何
不会修改数据成员的函数都应该声明为const类型
,相当于这个函数是个只读函数。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。 -
const成员函数只能调用const成员函数,不能调用非const成员函数,因为非const成员函数可能改变对象的数据成员
-
const只能修饰成员函数,普通函数不可以加const
-
在类中被const 声明的函数只能访问const 函数,而非const 函数可以访问任意成员函数,包括const 成员函数。
-
加上mutable修饰符的数据成员在任何情况下通过任何手段都可以进行修改,所以在const函数也是可以进行修改的
-
如果函数名、参数、返回值都相同的const成员函数和非const成员函数是可以构成重载,那么const对象调用const成员函数,非const对象默认调用非const的成员函数
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; // const成员函数
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void) const //GetCount仅仅只用作计数,不修改成员变量
{
++ m_num; // 编译错误,企图修改数据成员m_num
Pop(); // 编译错误,企图调用非const函数
return m_num;
}
- const修饰的成员函数:实际上修饰的是this指针指向空间的内容不可被修改
- 有了const修饰的成员函数,其this指针类型:const Date* const (
左定值
,右定向) - 而普通的成员函数:可以修改成员变量 ,其this指针类型:Date* const
二、static
-
1)
用static修饰局部变量:
- 使得局部变量由自动存储期变为静态存储期,生存期变得和全局变量一样,直到程序结束才释放,
- 但是并不是将该变量声明为全局变量,其他函数内也无法使用,也就是说改变了的存储位置,生存期发生了改变,但是作用域并没有改变,任然是局部变量作用域
初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
-
2)
用static修饰全局变量:
使其只在本文件内部有效,而其他文件不可连接或引用该变量。(注意:普通全局变量可以在其他.c文件中使用,只需要extern关键字,在链接的时候会找到该变量的地址)
- 还是静态存储区,在整个程序运行期间一直存在。
初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
-
3)
用static修饰函数:
- 在定义函数的时候,返回值类型前面加 static 修饰。这样的函数 被称为静态函数。
- 对函数的链接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的
(这一点在大工程中很重要很重要,函数的定义和声明在默认情况下都是extern的)。
- 使用静态函数的
好处是,不用担心与其他文件的同名函数产生干扰
,另外也是对函数本身的一种保护机制。 - 注意:不要在头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;
-
4)
类的静态成员
- 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,
静态成员是类的所有对象中共享的成员,而不是某个对象的成员
。对多个对象来说,静态数据成员只存储一处,供所有对象共用
- 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,
-
5)
类的静态成员函数
- 静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
- 在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);
声明时加上static修饰,实现的时候也不需要static 的修饰
,因为static是声明性关键字;类的静态函数是在该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,它就是增加了类的访问权限的全局函数:
//类的静态函数
class A
{
private:
static void func(int value);
};
//实现
void A::fun(int);
三、try{}catch(){}
- 阅读:C++57个入门知识点_57 异常处理(为了程序健壮性,使用try{}catch(){}将代码包裹,出现异常时可以被程序接到,而不会造成程序阻塞或崩溃;需要在VS中打开设置;exception类)
参考博文: