基本概念
命名空间&域
命名空间:使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染。解决C语言的缺陷命名冲突。
用命名空间去访问冲突的变量名用于全局变量。
命名空间可以嵌套。
域:影响生命周期和访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 /*全部展开*/
using namespace std;
int main()
{
cout << "hello" << endl;
}
/*指定访问*/
int main()
{
std::cout << "hello" << std::endl;
}
/*指定展开*/
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
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
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
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
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
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
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();//可以,权限的缩小
}引用和指针的区别
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 >【典型错解】
>==①宏是一种替换,不需要传参。==
==②不需要return。==
==③不需要;。==
==④宏是替换,需要考虑替换完之后计算的优先级,所以需要用()括住每个参数。==
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 = 0
和static int x = 0
分别声明了两种不同类型的变量,它们的主要区别在于以下几个方面:
- 存储类别(Storage Class):
const int x = 0
:这是一个具有常量存储类别的变量。它将在程序运行时分配内存空间,但其值不能被修改。这种变量在编译时被分配内存,通常存储在程序的数据段(data segment)中。static int x = 0
:这是一个具有静态存储类别的变量。静态变量在程序的生命周期内都存在,它们在程序开始运行时分配内存并在程序结束时释放。静态变量通常存储在程序的数据段中。
- 生命周期:
const int x = 0
:这种变量的生命周期与程序的生命周期相同。它在程序启动时创建,在程序结束时销毁。
static int x = 0
:静态变量的生命周期也与程序的生命周期相同。它在程序启动时创建,在程序结束时销毁。与
const
变量不同,静态变量可以在其生命周期内多次被访问和修改。
- 可修改性:
const int x = 0
:该变量的值是不可修改的,因为它被声明为常量。任何尝试修改它的操作都会导致编译错误。static int x = 0
:静态变量的值可以在程序的不同地方修改,但它的生命周期内只有一个实例,因此在程序的多个地方共享相同的值。总结:
const int x = 0
创建一个不可修改的常量。static int x = 0
创建一个具有静态存储类别的变量,它的生命周期与程序的生命周期相同,并且可以在多个地方共享相同的值。这两者的主要区别是可修改性和存储类别。