基本概念

命名空间&域

命名空间:使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染。解决C语言的缺陷命名冲突。

用命名空间去访问冲突的变量名用于全局变量。

命名空间可以嵌套。

域:影响生命周期和访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
/*---域---*/
int a = 0;//全局域
namespace bit//命名空间域
{
int a = 1;
}

/*展开命名空间域*/
//using namespace bit;

int main()
{
int a = 2;//局部域
printf("%d\n",a);//访问优先:局部域->全局域->展开了命名空间域 or 指定访问命名空间域,不会主动去空间域搜索。
printf("%d\n", ::a);//全局搜索。
printf("%d\n", bit::a);//指定访问命名空间域。
return 0;
}

头文件

#include<iostream>

using namespace std;

直接展开会有风险,项目里面最好别直接展开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*全部展开*/
#include<iostream>
using namespace std;
int main()
{
cout << "hello" << endl;
}

/*指定访问*/
#include<iostream>
int main()
{
std::cout << "hello" << std::endl;
}

/*指定展开*/
#include<iostream>
using std::cout;
using std::endl;
int main()
{
cout << "hello" << endl;
}

运算符

留插入运算符 cout <<

留提取运算符 cin >>

缺省参数

从左到右依次传参。

全缺省

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
void func(int a = 10, int b = 20,int c=30)
{
cout << a+b+c << endl;
}

int main()
{
func(1,2);//1+2+30
func();//10+20+30
}

半缺省

1
2
3
4
5
6
#include<iostream>
using namespace std;
void func(int a, int b = 20,int c=30)/*仅可左边部分缺省*/
{
cout << a+b+c << endl;
}

函数重载

一个函数多个意思

  • 参数类型不同
  • 参数数量不同
  • 参数顺序不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;

}
int main()
{
f();
f(10);
return 0;
}

仅返回值不同不构成重载

C++支持,但C不支持重载。

引用

基本情况

“取别名”

1
2
3
4
5
	int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
//地址是完全相同哒哒

引用不改变指向

1
2
3
4
5
6
7
int a = 0;
int& nickname = a;//这里的&表示引用
cout << &a << endl;
cout << &nickname << endl;//这里的&表示取地址

int john=1;
nickname= john;//此处并不是改为别人的别名,而是将最初的数据赋值

简化C语言指针操作

1
2
3
4
5
6
7
8
void Swap(int& a, int& b)//如果是交换地址的话是 int*&
{
//改变a,就改变x的数值
}
int main()
{
Swap(x, y);
}

引用的应用

定义结构体的写法,作为参数。

1
2
3
4
5
6
7
struct Node
{
int data;
Node* next;//需要设计&list去连接下一个结点的指针。
};
typedef Node* List;
void **InitList**(List &list)

节省运行时间

应用做参数可以提高效率。大对象/深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <ctime>
using namespace std;
struct A
{
int a[10000];
};

void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
A a;

// 以值作为函数参数
clock_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
clock_t end1 = clock();

// 以引用作为函数参数
clock_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
clock_t end2 = clock();

// 分别计算两个函数运行结束后的时间
double elapsed1 = double(end1 - begin1) / CLOCKS_PER_SEC;
double elapsed2 = double(end2 - begin2) / CLOCKS_PER_SEC;

cout << "TestFunc1(A)-time:" << elapsed1 << " seconds" << endl;
cout << "TestFunc2(A&)-time:" << elapsed2 << " seconds" << endl;
}

int main()
{
TestRefAndValue();
return 0;
}

引用做返回值,减少拷贝,提高效率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <time.h>

struct A{ int a[10000]; };
A a;

// 值返回
A TestFunc1() { return a;}
// 引用返回
A& TestFunc2(){ return a;}

void TestReturnByRefOrValue()
{
// 以值作为函数的返回值类型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作为函数的返回值类型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 计算两个函数运算完成之后的时间
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

==引用做返回值需要注意:在任何情况都可传参。但谨慎用引用做返回值,出了函数作用域,对象就不在了,就不能用应用返回;还在(static),就可以用引用返回。==

引用做返回值,可以同时读+写返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<bits/stdc++.h>
using namespace std;
struct SeqList{
int a[100];
size_t size;

int& at(int pos)
{
assert(pos>=0&&pos<100);
return a[pos];
}

int& operator[](int pos)
{
assert(pos>=0&&pos<100);
return a[pos];
}
};

int main()
{
SeqList s;
s.at(0)=9;//引用做返回值,可以直接写入a[pos]=9
s.at(0)++;//a[pos]+1
cout<<s.at(0)<<endl;//读取返回值

s[1]=10;
s[1]++;
cout<<s[1]<<endl;
}

引用的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
int main()
{
const int a=0;
int&b=a;//不可以,const表示该变量的值是不可修改的,int&b=a是对权限的放大

const int c=0;
int d=c;//可以,这里是c拷贝给d,d的改变不会影响c


int a=0;
const int&b=a;//权限可以缩小,缩小b作为别名的权限,只读但不可修改

double a=1.11;
int b=a;//可以,类型转换
int&c=a;//不可以,发生类型转换会产生临时变量,即由a产生一个临时变量具有常性质(相当于该变量被const修饰)。再变为c的别名导致权限的放大
const int&b=a;//可以,临时变量的常性
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int func1()
{
static int x=0;
return x;
}
int& func2()
{
static int x=0;
return x;
}
int main()
{
int&ret1=func1();//不可以,func1()函数返回临时变量,具备常性
const int&ret1=func1();//可以,权限的平移

int&ret2=func2();//可以,func2()函数返回x的别名。具备读写权限权限的平移
const int&ret2=func2();//可以,权限的缩小
}

引用和指针的区别
img

auto

自动推导数据的类型

1
2
3
4
5
6
7
8
9
10
>int main()
>{
int a=0;
int b=0;
auto c=a;
auto d=1+1.11;
cout<<typeid(c).name()<<endl;
cout<<typeid(d).name()<<endl;
return 0;
>}

auto访问数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>int main()
>{
/*访问数组元素的方法——旧方法*/
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
cout << *p << " ";
cout<<endl;

/*访问数组元素的方法——语法糖*/
for (auto e:array)//依次取数组中的元素,赋值给e。自动迭代,自动判断结束
{
cout<<e<<" ";
}
cout<<endl;

for (auto& e:array)//改变数组里的数据,需要取引用
{
e*=2;
}

for (auto e:array)//再次打印
{
cout<<e<<" ";
}
cout<<endl;
>}

内联函数

反复调用函数很多次,会创建很多个栈帧。

1
2
3
4
5
6
7
8
9
10
11
>int Add(int x,int y)
>{
return (x+y)*10;
>}
>int main()
>{
for(int i=0;i<10000;i++)
{
cout<<Add(i,i+1)<<endl;
}
>}

利用宏函数可以优化【高频易错】:

将上述Add用宏表述

1
2
>【典型错解】
>#define Add(int x,int y) return ((x+y)*10);

==①宏是一种替换,不需要传参。==

==②不需要return。==

==③不需要;。==

==④宏是替换,需要考虑替换完之后计算的优先级,所以需要用()括住每个参数。==

1
>#define Add(x,y) ((x)+(y))*1

宏函数

优点:不需要建立栈帧,提高调用效率。

缺点:复杂,容易出错,不能调试。


内联函数,在函数名前面加inline

适合内联函数的函数特点:函数比较短,多次反复调用

1
2
3
4
5
6
7
8
9
10
11
12
>inline int Add(int x,int y)
>{
return (x+y)*10;
>}
>int main()
>{
for(int i=0;i<10000;i++)
{
cout<<Add(i,i+1)<<endl;
}
return 0;
>}

函数过长,会导致代码膨胀,编译器会忽视内联。

空指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>void f(int)
>{
>cout<<"f(int)"<<endl;
>}
>void f(int*)
>{
>cout<<"f(int*)"<<endl;
>}
>int main()
>{
>f(0);//f(int)
>f(nullptr);//f(int*)
>f((int*)NULL);//f(int*)
>return 0;
>}

复习const&static的区别

const int x = 0static int x = 0 分别声明了两种不同类型的变量,它们的主要区别在于以下几个方面:

  1. 存储类别(Storage Class):
  • const int x = 0:这是一个具有常量存储类别的变量。它将在程序运行时分配内存空间,但其值不能被修改。这种变量在编译时被分配内存,通常存储在程序的数据段(data segment)中。
  • static int x = 0:这是一个具有静态存储类别的变量。静态变量在程序的生命周期内都存在,它们在程序开始运行时分配内存并在程序结束时释放。静态变量通常存储在程序的数据段中。
  1. 生命周期:
  • const int x = 0:这种变量的生命周期与程序的生命周期相同。它在程序启动时创建,在程序结束时销毁。

  • static int x = 0:静态变量的生命周期也与程序的生命周期相同。它在程序启动时创建,在程序结束时销毁。

    const 变量不同,静态变量可以在其生命周期内多次被访问和修改。

  1. 可修改性:
  • const int x = 0:该变量的值是不可修改的,因为它被声明为常量。任何尝试修改它的操作都会导致编译错误。
  • static int x = 0:静态变量的值可以在程序的不同地方修改,但它的生命周期内只有一个实例,因此在程序的多个地方共享相同的值。

总结:

  • const int x = 0 创建一个不可修改的常量。
  • static int x = 0 创建一个具有静态存储类别的变量,它的生命周期与程序的生命周期相同,并且可以在多个地方共享相同的值。这两者的主要区别是可修改性和存储类别。