C++对C语言的加强  

namespace命名空间

C++命名空间基本常识

我是红色字体
  所谓 namespace,是指标识符的各种可见范围C++ 标准程序库中的所有标识符都被定义于一个名为 stdnamespace 中。

  一:<iostream><iostream.h>格式不一样,前者没有后缀。实际上在编译器中,二者是两个不同的文件。后缀为 .h 的头文件在 C++ 标准中已经明确提出不支持了,早些的实现将标准库功能都定义在全局空间内部,声明在在带 .h 后缀的头文件里,C++ 标准为了和 C 区别开,也为了正确使用命名空间,规定头文件不使用后缀 .h。  因此

  1. 当使用 <iostream.h> 时,相当于在 C 中调用库函数,使用的是全局命名空间,也就是早期的 C++ 实现;
  2. 当使用 的时候,该头文件没有定义全局命名空间,必须 使用namespace  std ;这样才能正确使用 cout/cin;

  二:由于 namespace 的概念,使用 C++ 标准程序库的任何标识符时,可以有三种选择:

  1. 直接指定标识符。例如 std::ostream 而不是 ostream。完整语句如下
std::cout << std::hex << 3.4 <<	std::endl;
  1. 使用 using 关键字
using std::cout;
using std::endl;
using std::cin;
cout << std::hex << 3.4 << endl;
  1. 最方便的就是使用 using namespace std;例如:using namespace std ;这样命名空间 std 内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:  
using namespace std
cout << hex << 3.4 << endl;

  因为标准库非常的庞大,所以程序员在选择的类的名称或函数名  时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间 std 中。但这又会带来了一个新问题。无数原有的 C++ 代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了 <iostream.h><iostream> 等等这样的头文件,一个是为了兼容以前的 C++ 代码,一个是为了支持新的标准。命名空间 std 封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加.h ,但是 C++ Primer Plus 中好像不建议直接在全局使用这种东西。 

C++命名空间定义以及使用方法

  在 C++ 中,名称 name 可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的 C++ 库时,这些标识符的命名发生冲突

  标准 C++ 引入了关键字 namespace(命名空间/名字空间/名称空间/名域),可以更好地控制标识符的作用域。

   stdC++ 标准命名空间,C++ 标准程序库中的所有标识符都被定义在 std 中,比如标准库中的类 iostreamvector 等都定义在该命名空间中,使用时要加上 using声明using namespace stdusing指示(如std::stringstd::vector<int>).  

C 语言中的命名空间
    1. 在 C 语言中只有一个全局作用域
    2. C 语言中所有的全局标识符共享同一个作用域
    3. 标示之间可能发生冲突

C++ 中的命名空间
    1. 命名空间将全局作用域分为不同的部分
    2. 不同命名空间中的标识符可以同名而且不会发生冲突(二义性)
    3. 命名空间可以互相嵌套
    4. 全局作用域也叫默认命名空间

C++ 命名空间的定义与使用

#include<iostream>
namespace myspace { //定义自己的命名空间
    int a = 0;
}
int main(void){
    using namespace std;//使用整个标准命名空间
    cout << myspace::a << endl;  //使用特定命名空间内的成员
    return 0;
}

“实用性”增强

#include<iostream>
//C语⾔言中的变量都必须在作⽤用域开始的位置定义!!
//C++中更强调语⾔言的“实⽤用性”,所有的变量都可以在需要使⽤用时再定义
int main(void){
    using namespace std;
    int i = 0;
    cout << "i = " << i << endl;
    int k = 4;
    cout << "k = " << k << endl;
    return 0;
}

变量检测增强

在C语⾔言中,重复定义多个同名的全局变量是合法的
在C++中,不允许定义多个同名的全局变量
C语⾔言中多个同名的全局变量最终会被链接到全局数据区的同⼀一个地址空间上
int a;
int a = 1;
C++直接拒绝这种二义性的做法

struct 类型增强

#include<iostream>
/*
C语⾔言的struct定义了⼀一组变量的集合, C编译器并不认为这是⼀一种新的类型
C++中的struct是⼀一个新类型的定义声明
*/

int main(void){
    using namespace std;
	struct Student {
		char name[100];
		int age;
	};
	Student s1 = { "wang", 1 };
	Student s2 = {"wang2", 2};
    return 0;
}

C++中所有变量和函数都必须有类型

/*
	C++中所有的变量和函数都必须有类型
	C语⾔言中的默认类型在C++中是不合法的

	函数f的返回值是什么类型,参数⼜又是什么类型?
	函数g可以接受多少个参数?
*/
//在 C 语言中是可以通过编译的,但是 C++ 中不可以
f(i){
	printf("i = %d\n", i);
}

g(){
	return 5;
}

int main(){
	f(10);
	printf("g() = %d", g(1, 2, 3, 4));
	return 0;
} 
在 C 语言中
    int f(); 表示返回值为 int,接收任意参数的函数
    int f(void); 表示返回值为 int 的无参函数

在 C++ 中
    int f(); 和 int f(void) 都表示范围值为 int 的无参函数,且更推荐使用后者编程
```cpp
#include<iostream>
/*
	C++中的布尔类型
	C++在C语⾔言的基本类型系统之上增加了bool
	C++中的bool可取的值只有true和false
		理论上bool只占⽤用一个字节,
		如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现

	true代表真值,编译器内部⽤用1来表⽰示
	false代表⾮非真值,编译器内部⽤用0来表⽰示

	bool类型只有true(⾮非0)和false(0)两个值
	C++编译器会在赋值时将⾮非0值转换为true, 0值转换为false
  */

int main(void){
    using namespace std;
	int a;
	bool b = true;
	cout << "b = " << b << " sizeof(b) = " << sizeof(b) << endl;
	b = 4;
	a = b;
	cout << "a = " << a << endl;
	cout << "b = " << b << " sizeof(b) = " << sizeof(b) << endl;
	
    return 0;
}

三目运算符功能增强

C 语言返回变量的值 C++ 语言是返回变量本身(引用)
1. C 语言返回变量的值 C++ 语言是返回变量本身
	C语言中的三目运算符返回的是变量值,不能作为左值使用
	C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方

2. 注意:三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用
	(a < b ? 1 : b ) = 30;

3. C语言如何支持类似C++的特性呢?
	当左值的条件:要有内存空间;C++编译器帮助程序员取了一个地址而已

const增强

指针常量与常量指针

#include<iostream>

int main(void){
    using namespace std;
	//const 定义常量 --> const 意味只读

	//第一个第二个意思一样 代表一个常整形数,且必须初始化
	const int a = 10;
	int const b = 20;

	//c是⼀一个指向常整形数的指针(所指向的内存数据不能被修改,但是本⾝身可以修改)
	const int* c = NULL;

	//d	常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
	int* const d = NULL;

	//e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
	const int* const e = NULL;

    return 0;
}

/*
按照 * 与 const 的位置来区分是 指针常量,还是常量指针。
当 const 在 * 之前时,为常量指针,const 修饰 *p,*p不可被更改,但是 p 可以,也就是可以改变指向
当 const 在 * 之后时,为常量指针,const 修饰 p,p不可被更改,但是 *p 可以,也就是可以改变指向的值
我是这么区分的
*/

合理利用 const 的好处

1. 指针做函数参数,可以有效的提高代码可读性,减少bug;
2. 清楚的分清参数的输入和输出特性

const 的原理

在 C 语言中有如下问题

#include <stdio.h>
int main(){
	const int a = 10;
	int *p = &a;  //通过指针修改了一个常量的值,这是我们所不期望的,并不是一个合格的常量
	*p = 20;
	printf("a = %d\n", a);  //此时 a 已经变为 20
	return 0;
}

在 C++ 中使用同样的方法时

#include<iostream>
int main(void){
    using namespace std;
    const int a = 10;
    int* p = (int*)&a;
    *p = 20;  //改变的时临时开辟的 temp 的变量
    cout << "a = " << a << endl;  // a 并没有改变
    cout << "*p = " << *p << endl;  //*p 被改变了
    return 0;
}

  在 C++ 中的当编译器发现有一个 const 修饰的常量时,会将该变量放在全局区的符号表内,里面有一个键值对用来存放符号以及相应的值,常量没有空间,也就是地址,在只读区域的 .text 段内。当执行上述对一个常量取地址时,编译器会临时开辟一个空间 temp,让这个指针存放这个临时空间的地址。直接使用 a 常量时,编译器会将其替换为在符号表中的值,此过程类似于 #define,去边后面会讲。

const 与 #define 的异同

相同
C++ 中 const 常量在使用的过程中于 #define 都是直接将变量进行替换

不同
1. 就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用

2. 就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误

3. 就存储方式而言:#define 只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const 定义的只读变量在程序运行过程中只有一份备份

4. 从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了

总结

C 语言中的 const 变量
    C 语言中 const 变量是只读变量,有自己的存储空间

C++ 中的 const 常量
    1. 可能分配存储空间,也可能不分配存储空间
    2. 当 const 常量为全局,并且需要在其它文件中使用,会分配存储空间
    3. 当使用 & 操作符,取 const 常量的地址时,会分配存储空间
    4. 当const int &a = 10; const 修饰引用时,也会分配存储空间