1. ::作用域运算符
作用域云算符 用来访问某个作用域里面的成员
:: 前无命名空间名 表示访问全局的
:: 前有命名空间名 表示访问该命名空间的成员
通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量
//全局变量
int a = 10;
void test(){
//局部变量
int a = 20;
//全局a被隐藏
cout << "a:" << a << endl;
}
程序的输出结果是a:20。在test函数的输出语句中,使用的变量a是test函数内定义的局部变量,因此输出的结果为局部变量a的值。
作用域运算符可以用来解决局部变量与全局变量的重名问题
/全局变量
int a = 10;
//1. 局部变量和全局变量同名
void test(){
int a = 20;
//打印局部变量a
cout << "局部变量a:" << a << endl;
//打印全局变量a
cout << "全局变量a:" << ::a << endl;
}
这个例子可以看出,作用域运算符可以用来解决局部变量与全局变量的重名问题,即在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。
2. 命名空间
2.1 命名空间使用语法
创建一个命名空间:
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
void test(){
cout << "A::a : " << A::a << endl;
cout << "B::a : " << B::a << endl;
}
命名空间只能全局范围内定义
// ERROR
void test()
{
// 错误
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
cout << "A::a : " << A::a << endl;
cout << "B::a : " << B::a << endl;
}
命名空间可嵌套命名空间
namespace A{
int a = 10;
namespace B{
int a = 20;
}
}
void test(){
cout << "A::a : " << A::a << endl;
cout << "A::B::a : " << A::B::a << endl;
}
命名空间是开放的,即可以随时把新的成员加入已有的命名空间中,但新成员只能在加入后使用
namespace A{
int a = 10;
}
namespace A{
void func(){
cout << "hello namespace!" << endl;
}
}
void test(){
cout << "A::a : " << A::a << endl;
A::func();
}
2.2 using声明
using声明可使得指定的标识符可用
namespace A{
int paramA = 20;
int paramB = 30;
void funcA(){ cout << "hello funcA" << endl; }
void funcB(){ cout << "hello funcA" << endl; }
}
void test(){
//1. 通过命名空间域运算符
cout << A::paramA << endl;
A::funcA();
//2. using声明
using A::paramA;
using A::funcA;
cout << paramA << endl;
//cout << paramB << endl; //不可直接访问
funcA();
//3. 同名冲突
using A::paramB; // using声明相当于把A::paramB 在此处重新定义
//int paramB = 20; // 注意:using声明了某个变量,在该作用域内不能再定义同名变量
}
using声明碰到函数重载
namespace A{
void func(){}
void func(int x){}
int func(int x,int y){}
}
void test(){
using A::func;
func();
func(10);
func(10, 20);
}
如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合。
2.3 using编译指令
using编译指令使整个命名空间标识符可用
namespace E{
int paramA = 20;
void funcA(){ cout << "hello funcA" << endl; }
}
void test04(){
using namespace E;
cout << paramA << endl;
funcA();
//不会产生二义性
int paramA = 30;
cout << paramA << endl;
}
namespace F{
int paramA = 20;
void funcA(){ cout << "hello funcA" << endl; }
}
void test05(){
using namespace E;
using namespace F;
//二义性产生,不知道调用A还是B的paramA
//cout << paramA << endl;
cout << E::paramA << endl; // OK
}
注意:使用using声明或using编译指令会增加命名冲突的可能性。可在代码中使用作用域运算符来避免产生二义性
3. struct类型加强
- c中定义结构体变量需要加上struct关键字,c++不需要
- c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。
//1. 结构体中即可以定义成员变量,也可以定义成员函数
struct Student{
string mName;
int mAge;
void setName(string name){ mName = name; }
void setAge(int age){ mAge = age; }
void showStudent(){
cout << "Name:" << mName << " Age:" << mAge << endl;
}
};
//2. c++中定义结构体变量不需要加struct关键字
void test01(){
Student student;
student.setName("John");
student.setAge(20);
student.showStudent();
}
4. 更严格的类型转换
在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转
void test()
{
// 不能隐性转换,必须显性转换
char *p = (char *)malloc(64);
printf("%p", p);
}
5. 三目运算符功能增强
c语言三目运算表达式返回值为数据值,为右值,不能赋值
void test()
{
int a = 10;
int b = 20;
// C 语言的三目运算符是右值
*(a > b ? &a : &b) = 100;
printf("%d \n",b);
}
c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值
void test()
{
int a = 10;
int b = 20;
cout << (a>b?a:b) << endl;
// C++ 语言的三目运算符是左值
(a>b?a:b) = 100;
cout << b << endl;
}
[左值和右值概念]
在c++中可以放在赋值操作符左边的是左值,可以放到赋值操作符右面的是右值。
有些变量即可以当左值,也可以当右值。
左值为Lvalue,L代表Location,表示内存可以寻址,可以赋值。
右值为Rvalue,R代表Read,就是可以知道它的值。
比如:int temp = 10; temp在内存中有地址,10没有,但是可以Read到它的值。
6. C/C++中的const
6.1 C 语言的const
- C语言的const修饰的变量都有空间
- C语言的const修饰的全局变量具有外部链接属性
// 1.C语言的const修饰的变量都有空间
// 2.C语言的const修饰的全局变量具有外部链接属性
const int a = 10; // 常量区,一旦初始化,不能修改
void test01() {
// a = 200; // 全局的const不能直接修改
int *p = (int *)&a;
// *p = 100; // 全局的const不能间接修改
printf("%d \n",a);
}
void test02() {
const int b = 30; // 栈区
// b = 200; // 局部的const修饰的变量不能直接修改
int *p = (int *)&b;
*p = 300; // 局部的const修饰的变量可以间接修改
printf("b的地址 %p %d\n",&b,b);
printf("p指向的地址 %p %d \n",p,*p);
}
6.2 C++语言的const
- C++ 语言的const修饰的变量有时有空间,有时没有空间
- C++ 语言中const修饰的全局变量具有内部链接属性
c语言全局const会被存储到只读数据段。c++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。
1.对于基础数据类型,也就是const int a = 10这种,编译器会进行优化,将值替换到访问的位置。
// 1.对于基础数据类型,也就是const int a = 10这种,编译器会进行优化,将值替换到访问的位置
void test03()
{
const int constA = 10;
int* p = (int*)&constA;
*p = 300;
// 发生常量折叠 在编译阶段 编译器:cout << "constA:" << 10 << endl
cout << "constA:" << constA << endl; // 10
cout << "*p:" << *p << endl; // 30
}
- 对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。
// 2.对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。
void test04()
{
int b = 10;
const int constA = b;
int* p = (int*)&constA;
*p = 300;
cout << "constA:" << constA << endl; // 300
cout << "*p:" << *p << endl; // 300
}
- 对于自定数据类型,比如类对象,那么也会分配内存。
class Person
{
public:
Person(){
}
public:
int age;
};
void test05()
{
const Person person; //未初始化age
//person.age = 50; //不可修改
Person* pPerson = (Person*)&person;
//指针间接修改
pPerson->age = 100;
cout << "pPerson->age:" << pPerson->age << endl; // 100
pPerson->age = 200;
cout << "pPerson->age:" << pPerson->age << endl; // 200
}
c中const默认为外部连接,c++中const默认为内部连接.当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10;
6.3 尽量以const替换#define
1.const 有类型,可进行编译器类型安全检查,#define 无类型,不可进行类型检查
#define MA 128
const short ma = 128;
void func(short a){
cout << "func(short a)" << endl;
}
void func(int a){
cout << "func(int a)" << endl;
}
void test01() {
func(MA); // func(int a)
func(ma); // func(short a)
}
2.const 有作用域,而#define 不重视作用域,默认定义处到文件结尾,不能限定常量的使用范围,如果定义在指定作用域下有效的常量,那么#define就不能用
#define b 10
void test() {
const int a = 10;
cout << a << endl; // 只能在test函数内访问a
}
void test02() {
test();
cout << b << endl;
// cout << a << endl; a 不可以访问
}
网友评论