目录
前言
今天我们就要来学习C++的知识了,通过C语言来过度到C++,首先我们先来了解一下C++中的入门语法,比如命名空间,缺省参数,函数的重载等知识。
一、什么是C++
C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。
1.1 C++关键字(C++98)
![](https://img-blog.csdnimg.cn/direct/13e4685b07f8467095accce0f2ff09e7.png)
二、命名空间
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
printf("%d\n", rand);
return 0;
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
2.1 命名空间定义
1.正常命名空间的定义
namespace bit
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
2.命名空间的定义可以嵌套
//2. 命名空间可以嵌套
// test.cpp
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
3.同名的命名空间会合并
3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
// ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个
// test.h
namespace N1
{
int Mul(int left, int right)
{
return left * right;
}
}
2.2 命名空间的使用
默认操作规则:局部——全局(如果通过using展开(声明)了命名空间,才会到命名空间查找)
namespace bit
{
// 命名空间中可以定义变量/函数/类型
int a = 0;
int b = 1;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 编译报错:error C2065: “a”: 未声明的标识符
printf("%d\n", a);
return 0;
}
int main()
{
printf("%d\n", N::a);
return 0;
}
2.使用using将命名空间中某个成员引入
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
3.使用using namespace 命名空间名称 引入
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
在比较大的工程项目中,一般不展开命名空间。
三、C++输入&输出
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
// 可以自动识别变量的类型
cin>>a;
cin>>b>>c;
cout<<a<<endl;
cout<<b<<" "<<c<<endl;
return 0;
}
四、缺省参数
4.1 缺省参数概念
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(10); // 传参时,使用指定的实参
return 0;
}
4.2 缺省参数分类
void Func(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
注意:
//a.h
void Func(int a = 10);
// a.cpp
void Func(int a = 20)
{}
// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
//用那个缺省值。
应用:我们可以在需要扩容的数据结构中用缺省参数给一个初始值,避免扩容次数过多。
五、函数重载
5.1 函数重载分类
1.参数类型不同
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
2.参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
3.参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
5.2 函数重载的原理
![](https://img-blog.csdnimg.cn/direct/cec927f1b045433d84fdc5e602aade58.png)
![](https://img-blog.csdnimg.cn/direct/27b552ed138f4c988f82b469697de6c5.png)
![](https://img-blog.csdnimg.cn/direct/0406ec23d5b74b63a5a6d9a73f7c5856.png)
所以为什么C++支持重载,而C语言不支持重载?
因为在链接过程中,C语言直接通过函数名去查找,所以不支持重载。而C++通过函数名字修饰规则修饰后的函数名去查找的,所以支持重载。
六、引用
6.1 引用概念
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
6.2 引用的特性
void TestRef()
{
int a = 10;
// int& ra; // 该条语句编译时会出错 必须初始化
int& ra = a;
int& rra = a;
printf("%p %p %p\n", &a, &ra, &rra);
}
6.3 常引用
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;//隐式类型转换
}
其中权限可以缩小,平移,就是不可以放大。
const int& rd =d发生了隐式类型转换,相当于把d给rd时会产生一个int类型的临时变量,然后再把这个变量给rd,这个变量具有常性,所以要加const,不加也相当于权限的放大。
6.4 使用场景
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
2.做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
6.5 传值、传引用效率比较
#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
可以看到传引用比传值效率跟高。
6.6 引用和指针的区别
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0;
}
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
七、内敛函数
7.1 概念
![](https://img-blog.csdnimg.cn/direct/2a119a4b10d342f09e271980108ec4a5.png)
![](https://img-blog.csdnimg.cn/direct/1fe0ca6f266e4f168b387c3ae2bcc986.png)
7.2 特性
八、auto关键字
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl; //int
cout << typeid(c).name() << endl; //char
cout << typeid(d).name() << endl; //int
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}
其中b,c,d的类型都是auto通过值的类型自动推导出来的。
注意:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto 的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
8.1 auto的使用细则
1. auto与指针和引用结合起来使用
int main()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
cout << typeid(a).name() << endl; //int*
cout << typeid(b).name() << endl; //int*
cout << typeid(c).name() << endl; //int
*a = 20;
*b = 30;
c = 40;
return 0;
}
2. 在同一行定义多个变量
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
8.2 auto不能推导的场景
1. auto不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
2. auto不能直接用来声明数组
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
九、基于范围的for循环(C++11)
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
因为传递的是数组首元素的地址,所以不能确定范围
十、指针空值nullptr(C++11)
在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
所以NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何 种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
总结
上述文章,我们讲了一些C++的一些基础语法,希望对你有所帮助。