目录
二、命令空间简介、基本输入输出
1、命名空间
2、基本输入输出cin、cout精解
三、auto、头文件防卫、引用、常量
(1)局部变量及初始化
(2)auto:变量的自动类型推断
(3)头文件防卫式声明
(4)常量(const与constexpr)
const
constexpr
二、命令空间简介、基本输入输出 1、命名空间
- (1)简介
同名实体定义:同名函数、同名变量、同名的类定义;
命名空间就是为了防止名字冲突而引入的一种机制。系统中可以定义多个命名空间,每个命名空间都有自己的名字,不可以同名;
可以把命名空间看成一个作用域。在这个命名空间定义函数,和在另一个命名空间定义的函数,即使同名,也互不影响;
- (2)定义
namespace 命名空间名
{
}
命名空间的定义可以不连续,甚至可以写在多个.cpp文件中,如果以往没有定义这个命名空间,那么 namespace 命名空间名 就相当于定义一个命名空间;如果以往定义了命名空间,那就相当于打开已经存在的命名空间并为其添加新成员的声明。
- (3)外界如何访问命名空间中的变量、函数
命名空间名::实体名。
其中,::叫作用域运算符
- (4)使用命名空间
using namespace 命名空间名
这样以后使用命名空间中的实体就不必再加 (命名空间名::)了 。
- (1)iostream库:输出输出流
流:就是一个字符序列
cout:console output,是个对象,"标准输出"对象,我们就认为这个对象是屏幕
<< :输出运算符,c++对其进行了重载,
<< :可以当成函数,有参数,例如:cout<<1。参数1在左边,就是cout对象;第二个参数就是 << 右边的实体。可以理解为将 参数2实体 送入参数1(即cout对象,屏幕)
endl:模版函数名,相当于函数指针
endl作用:换行、强制刷新输出缓冲区
输出缓冲区:一段内存,cout输出的时候是往输出缓冲区输出内容,那么输出缓冲区什么时候把内容输出到屏幕去的呢?
- 1.缓冲区满了
- 2.程序执行到main的return语句
- 3.调用了endl,能够强制刷新输出缓冲区(把缓冲区的内容往屏幕上输出)
- 4.当系统不太繁忙的时候,系统也会查看缓冲区内容,发现新内容也会正常输出到屏幕
<< 定义:ostream& std::cout.operator<<();
<< 返回的是一个写入了给定值(参数2)的cout对象。
- (2)std::cin
cin:标准输入,"iostream"相关对象,可以就理解成键盘
>> :输入运算符。返回其左侧运算对象作为其计算结果
cin >> val1 >> val2; --> (cin>>val1)>>val2。
C++中,局部变量随时用到随时定义 。
int i={ 5 }; //定义变量的同时初始化变量(c++ 11 ),=可有可无
int a[]{ 1,2,3,4,5 }; //定义数组的同时初始化数组
好处:int a = 3.5f; //可以编译成功
int a{ 3.5f }; //无法编译成功,因为3.5f是浮点数,而a是整形
(2)auto:变量的自动类型推断
auto可以在声明变量时,根据变量初始值的类型自动为此变量选择匹配的类型(声明时要赋初值)。 auto自动类型推断发生在编译期间,所以使用auto不会造成程序执行效率降低。
例如:
auto val = true;//bool
auto ch = 'a'; //char
(3)头文件防卫式声明
防止头文件重复包含。
// 法1
#ifndef 标识符 (如果没有用define定义过标识符,则运行程序段)
#define 标识符
程序段
#endif
//法2
#pragma once
(4)常量(const与constexpr)
const
C语言中的const:
C语言中const
修饰的变量是只读变量,在使用const
关键字声明定义变量时会给该变量分配内存空间。不能通过变量名直接修改该变量的值,但是可以通过指针的方式修改该变量对应的值,从某种意义上来说,C中const
修饰的变量不是真正意义上的常量,可以将其当作一种只读变量。
-
const
修饰全局变量时,该变量存储在文字常量区(只读),不能通过指针的方式修改其内容。const
修饰的全局变量默认是外部链接的,即其它源文件可以直接使用该变量(但是需要声明一下)。 -
const
修饰局部变量,该变量存储在栈区中(可读可写),通过指针的方式可以修改其内容
//fun.c
const int global_a=100; //global_a的本质是只读的变量,存放在文字常量区(内存空间只读)
// test.c
// 对fun.c中的global_a进行声明(不要赋值)
extern const int global_a;
int main()
{
const int a=10;
int* point_a=&a;
a=20; //error
global_a=200; //error
*point_a = 20; //ok
*point_a=&global_a;
*point_a=200; //error
return 0;
}
C++中的const:
C++中 const
修饰的变量不需要创建内存空间,当碰见常量声明时,C++不会单独的给
它分配内存中,而是将他分配在符号表中,符号表的存在形式是一个名字对,比如定义常量const int data = 10;
,C++会在一张符号表中添加 name
为data
,value
为10的一条记录,如下图所示:
- 既然,
const
修饰的变量没有内存空间,所以在C++中const
修饰的变量才是真正意义上的常量。 - 编译过程中若发现使用常量则直接以符号表中的值替换(符号表不会分配内存) 。
- C++中定义声明的全局常量是内部链接的,只能作用于当前的整个文件中,如果想让其它源文件对该常量进行访问的,必须加
extern
关键字将该常量转换成外部链接。 - 编译过程中若发现对 const 使用了 extern(外部变量)或者 &取地址 *** 作符,则给对应的常量分配存储空间(兼容 C),这块空间用常量的值进行初始化。例如有常量a,当对a变量取地址的时候,C++编译器会为a单独的开辟一块内存空间,int* p = (int *)&a;然后这个内存空间赋给指针p,就是p指向这个内存空间。后续修改p指向的空间的值的时候,和常量a没有任何关系。
C 语言中 const 变量是只读变量,有自己的存储空间;
C++中的const常量,可能分配存储空间,也可能不分配存储空间;
- 对
const
常量取地址时会分配空间 - 使用变量的形式初始化
const
修饰的变量时会分配空间 const
修饰自定义数据类型(结构体、对象)时会分配空间
int a = 200;
const int b = a; // 系统直接为b开辟空间,不会把b放入到符号表中
int* p = (int*)&b;
*p = 3000;
cout << "*p = " << *p << endl; // 3000
cout << "b = " << b << endl; // 3000
// 4. const修饰自定义类型的变量,系统会分配空间
const Person per = { 100, "viktor" };
const除了修饰常量外,还可以有 变量只读 的意思。例如:
void func(const int num)
{
const int count = 24;
int array[num]; // error,num是一个只读变量,不是常量
int array1[count]; // ok,count是一个常量
int a1 = 520;
int a2 = 250;
const int& b = a1; //b 是一个常量的引用,所以 b 引用的变量是不能被修改的
b = a2; // error
a1 = 1314; //但 const 对于变量 a1 是没有任何约束的,a1 的值变了 b 的值也就变了
cout << "b: " << b << endl; // 输出结果为1314
}
- 参数 num 表示这个变量是只读的,但不是常量
- count 是一个常量,因此可以使用这个常量来定义一个静态数组
- 变量只读并不等价于常量
- 引用 b 是只读的,但是并不能保证它的值是不可改变的,也就是说它不是常量
const与#define:
- const是在编译期间确定好的;#define是在预处理阶段处理好的。
- const 常量是由 编译器处理的,提供类型检查和 作用域检查,
const
有类型,可进行编译器类型安全检查;#define
无类型,不能进行类型检查,宏定义由预处理器处理,单纯的文本替换。 - 宏的作用域是当前的整个文件,const的作用域以定义的情况决定
void fun1()
{
#define a 10
const int b = 20;
// #undef a 。卸载宏 a
// # undef .卸载所有宏,将宏定义作用域限制在 fun1 里(卸载宏)
}
void fun2()
{
printf("a = %d\n", a); //可以使用,a只是单纯的饿文本替换而已
//printf("b = %d\n", b); //不能使用,超出了b的作用域
}
constexpr
- C++11 中添加了一个新的关键字 constexpr,这个关键字是用来修饰常量表达式的。
- 所谓常量表达式,指的就是由多个(≥1)常量(值不会改变)组成并且在编译过程中就得到计算结果的表达式。
- 常量表达式和非常量表达式的计算时机不同,非常量表达式只能在程序运行阶段计算出结果,但是常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率;因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。
- C++11 中添加了 constexpr 关键字之后就可以在程序中使用它来修饰常量表达式,用来提高程序的执行效率。在使用中建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。
- 在定义常量时,const 和 constexpr 是等价的,都可以在程序的编译阶段计算出结果。
对于 C++ 内置类型的数据,可以直接用 constexpr 修饰,但如果是自定义的数据类型(用 struct 或者 class 实现),直接用 constexpr 修饰是不行的。
例如:
// 此处的constexpr修饰是无效的
constexpr struct Test
{
int id;
int num;
};
//要定义一个类常量对象,可以这样写
struct Test
{
int id;
int num;
};
int main()
{
constexpr Test t{ 1, 2 };
constexpr int id = t.id;
constexpr int num = t.num;
// t 是一个常量,因此它的成员也是常量
t.num += 100; // error,不能修改常量
cout << "id: " << id << ", num: " << num << endl;
return 0;
}
为了提高 C++ 程序的执行效率,我们可以将程序中值不需要发生变化的变量定义为常量。
- 1、使用 constexpr 修饰函数的返回值,这种函数被称作常量表达式函数。constexpr 修饰的函数的返回值,必须要满足以下几个条件:
- 函数必须要有返回值,并且 return 返回的表达式必须是常量表达式。
constexpr int func1() { constexpr int a = 100; return a; }
-
整个函数的函数体中,不能出现非常量表达式之外的语句(using 指令、typedef 语句以及 static_assert 断言、return 语句除外)。
// error constexpr int func1() { constexpr int a = 100; constexpr int b = 10; for (int i = 0; i < b; ++i) { cout << "i: " << i << endl; } return a + b; } // ok constexpr int func2() { using mytype = int; constexpr mytype a = 100; constexpr mytype b = 10; constexpr mytype c = a * b; return c - (a + b); }
- 函数必须要有返回值,并且 return 返回的表达式必须是常量表达式。
2、constexpr 修饰模板函数
如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。
#include
using namespace std;
struct Person
{
const char* name;
int age;
};
// 定义函数模板
template
constexpr T dispaly(T t)
{
return t;
}
int main()
{
struct Person p { "luffy", 19 };
//普通函数
struct Person ret = dispaly(p); //p是变量,实例化后的函数不是常量表达式函数,此时 constexpr 是无效的
cout << "luffy's name: " << ret.name << ", age: " << ret.age << endl;
//常量表达式函数
constexpr int ret1 = dispaly(250); //250是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
cout << ret1 << endl;
constexpr struct Person p1 { "luffy", 19 }; //p1是常量
constexpr struct Person p2 = dispaly(p1);//参数是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
cout << "luffy's name: " << p2.name << ", age: " << p2.age << endl;
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)