C++学习(语法篇)

命名空间

作用域的分类

全局作用域
局部作用域
类作用域
命名空间作用域

命名空间的定义和作用:

命名空间的主要作用是避免不同代码库或模块中的标识符发生冲突。通过将标识符放在不同的命名空间中,可以确保它们不会相互干扰,命名空间作用域指的是在命名空间中定义的标识符(如变量、函数、类等)可以被访问和使用的范围

命名空间的创建:

namespace k{
	int a = 1;
	namespace h {//嵌套创建命名空间
		int b;
	}
}


int main() {
	printf("%d", k::a);//命名作用域内的参数可以被调用;
    printf("%d",k::h::b);//调用嵌套命名作用域的参数(先在k中找到h再找到b)
}


命名空间的展开

那么什么是将namespace展开呢?这里就要提到using namespace ,已经创建好的一个命名作用域,如果使用using namespace...展开,命名空间域的内容(如变量,函数)就会被引入到全局作用域中,后续即便不使用"::"也可以引用里面的东西

using namespace k;

namespace k{
	int a = 1;
	namespace h {
		int b;
	}
}

int main() {
	printf("%d", a);
	printf("%d", h::b);}//被展开的是k,但是h没有被展开,而h现在位于全局作用域中,可以直接找到h,再用::找b

//或者

using namespace k;
using namespace h;


int main() {
	printf("%d", b);//就可以直接调用b了
}

include的作用

其实include就是隐式地把一个头文件的内容拷贝到当前文件中,而这个头文件里面是有他自己的命空间的,如果使用using namespace std(大多数库函数都是用std作为命名作用域的名字,而重名的namespace会自动合并),这个头文件的命名作用域就会被引入到全局中,因此就不许要使用std::了;一般在文件开头会加一个using namespace std;

可以在.h文件中就展开成全局作用域,后面包含该头文件的都不用展开(图1)

或者在源文件中先包含头文件再展开,但每个包含头文件的都需要再展开(图2)

仅包含不展开会报错(图3)

调用变量时的匹配顺序

表达式中的变量将优先从当前局部域查找,再从全局中查找(包括展 开的命名空间),只有指定了命名空间才能找到命名空间域中的变量

#include <iostream>
using std::cout;

namespace k {
	int a = 1;
	namespace h {
		int b;
	}
}

int a=2;

int main() {
	cout << a;//将输出2,同时如果展开k会报错(全局作用域将会出现两个a)
	return 0;
}

 项目中谨慎展开std

很多时候在项目中仍然会用”std::“来引用变量以防止后加入的头文件与自己之前写好的变量重名,或者指定某一个命名空间

#include <iostream>
using namespace k;
using namespace h;
using std::cout;//指定展开某个命名空间

int main() {
	int a = 1;
	cout << a;//可以使用cout了,而其他的就不行
	return 0;
}


c++的输入和输出

cout是输出cin是输入,endls是换行符(或"/n"),这些包含在<iostream>头文件中,空格是 " ",​​​​​​

#include <iostream>
using namespace std;

int main() {
	cout << "Hello world" << endl;
	int b; cin >> b;
	cout << b << endl;
	return 0;
}
//输出
//Hello world
//b的值

cin\cout和printf、scanf的区别

  1. cin和cout可以自动识别变量的类型,而printf和scanf需要手动输入

  2. cout依据数据类型有自己的默认精度,如果要指定精度会比较麻烦,此时选择printf较优
     

    #include <stdio.h>
    
    int main(void) {
        double x = 3.141592653589793;
        printf("%.2f\n", x);   // 输出: 3.14
        return 0;
    }
    
    #include <iostream>
    #include <iomanip>
    int main() {
        double x = 3.141592653589793;
        std::cout << std::fixed << std::setprecision(2) << x << '\n';  // 输出: 3.14
        return 0;
    }

  3. 同步:cin和cout默认和C风格的输入输出流(如printf和scanf)保持同步。这是为了兼容C和C++代码混用的情况。同步机制会使得每次进行输入输出操作时,都要进行额外的检查和同步操作,这增加了开销。而printf和scanf没有这种同步机制,它们直接操作底层的输入输出缓冲区,效率更高。开头写上std::ios_base::sync_with_stdio(false)以取消同步流从而增加效率


缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实  
参则采用该形参的缺省值,否则使用指定的实参

缺省参数在函数声明或定义中是从右往左依次设立的,因为函数调用时不能跳过某个参数使其对应缺省参数(也就是说函数调用时必须从左往右赋值,剩下的才能自动对应缺省参数)

void test(int a=1, int b=2, int c = 3) {
	cout << a << b << c;
}

//错误案例:
void test(int a =1, int b , int c = 3) {
	cout << a << b << c;
}//报错:默认实参不在形参列表的结尾

void test(int a = 1, int b = 2, int c ) {
	cout << a << b << c;
}//报错:默认实参不在形参列表的结尾

int main() {
	test(3, 3);//输出3 3 3 
	test(3,,3);//报错
}

另外声明和定义同时存在时,只能在声明中设置缺省参数

报错重定义,写成下面这样就好了

函数的重载

c++中允许两个同名的函数存在,通过区分它们的参数的顺序,参数的个数,以及参数的类型(注意不是参数的名字以及返回值的类型)从而来确定要调用哪一个函数

void ret(int a, double b) {
	cout << a << endl;
}

void ret(double b, int a) {
	cout << b << endl;
}

void ret(int a){
    cout<<a<<endl;
}

int main() {
	ret(1, 1.1);//输出1
	ret(1.1, 1);//输出1.1
    ret(2);//输出2
}

不构成重载的情况以及错误的情况

void f(int a, double b) {
	cout << "a" << endl;
}

void f(int b, double a) {
	cout << "a" << endl;
}//本质上是一致的 




void ret() {
	cout << "a" << endl;
}

void ret(int a=2) {
	cout << "a" << endl;//会产生歧义
}



int main(){
    ret();//当什么都不调用时,既满足触发缺省参数的条件又满足没有参数的条件,此时会产生歧义
}

函数重载只依赖参数(类型、个数、顺序)而不依赖返回值,根本原因在于编译器在调用点只能看到调用语句本身,而调用语句里没有返回值信息


引用

定义和应用

变量的别名,语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main() {
	int b = 2;
	int& a = b;
	int& c = a;//引用的引用
	a += 3;
	cout << b<<endl;//5
	c += 2;
	cout << b<<endl;//7
	cout << a<<endl;//7
	return 0;
}

引用作为参数

void ADD(int& a, int& b) {
	a += b;
	return;
}

int main() {
	int a = 2;
	int b = 3;
	ADD(a,b);
	cout << a << endl;//5
	return 0;
}
引用作为函数的返回值
int& ADD() {
	static int a = 3;//必须用一块额外的空间存储将要返回的值
	a += 4;
	return a;
}


int& ADD() {
	int a = 4;//如果写成这样,在返回时就会销毁a所处的空间,返回值引用的是a的空间就会引发错误
	a += 3;
	return a;
}


int main() {
	cout << ADD() << endl;//7
	return 0;
}

传值、传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直

接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效

率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低

如果是传引用的话,不会有拷贝的过程,效率高

 引用和指针的不同点

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何

一个同类型实体

4. 没有NULL引用,但有NULL指针

5. 在sizeof中含义不同引用结果为引用类型的大小,但指针始终是地址空间所占字节个数

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

7. 有多级指针,但是没有多级引用(引用的引用不是多级引用)

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

9. 引用比指针使用起来相对更安全                                                                                                        


内联函数

在C语言学习时有宏函数,有它自己特殊的的写法和注意事项,从而实现不另外开空间的函数调用,而C++中在函数定义前增加inline即可将函数展开为函数体,此时该函数也就具有了宏函数的优缺点

内联函数的优缺点

优点:

1.增强代码的复用性

2.提高性能

缺点:

1.不方便调试宏。(因为预编译阶段进行了替换)

2.导致代码可读性差,可维护性差,容易误用。

3.没有类型安全的检查 

内联函数注意事项                                                                                                    

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会

用函数体替换函数调用

2. inline对于编译器而言只是一个建议,一般建将函数规模较小是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

3.内联函数的声明和定义不能分开,不然会发生链接错误


auto关键字

根据右侧的表达式自动推断左侧的类型
int main() {
	int a = 3;
	auto c = a;
	cout << c << endl;//3
	return 0;
}
int main() {
	int a = 0;
	auto b = &a;
	auto* c = &a;//用auto声明指针类型时,用auto和auto*没有任何区别
	auto& d = a;//用auto声明引用类型时则必须加&
	return 0;
}

1.使用auto定义变量时必须对其进行初始化

2.当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译

器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

3.auto不能作为函数的参数

4.auto不能直接用来声明数组


范围for

struct k {
	int data;
	double data2;
};

int main() {
	int a[] = { 1,2,3,4,5 };
	for (int i = 0; i < 5; i++) {
		cout << a[i] << endl;
	}///这是一般的遍历

	for (auto temp : a) {
		cout << temp << endl;
	}//范围for,结构是定义变量 : 数组,自动将数组的值赋值给变量,并自动迭代自动判断结束;

	k b[] = { {1,1.1},{2,2.2},{3,3.3} };
	for (auto temp : b) {
		cout << temp.data << endl;//在这里temp是结构体类型
	}
	return 0;
}
for循环迭代的范围必须是确定的,右侧不能是个长度不定的数组

指针空值nullptr

在C++中NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量,因此要表示空指针最好使用nullptr

1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptrC++11作为新关键字引入

2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值