c语言是结构化和模块化语言,不适合解决复杂问题。20世纪80年代,为了解决软件危机 计算机界提出了OOP面向对象思想。
C++是一种面向对象的编程语言,祖师爷Bjarne Stroustrup本贾尼在20世纪80年代开发出这门语言,因为C++是基于C语言而产生的,从而命名为C++也叫cpp。c++支持大部分c的语法, 所以c++既可以进行c语言得过程化程序设计,又可以进行面向对象的程序设计。
在学习c++之前 ,应该对c语言有很好的掌握。
开发工具
和c语言一样 仍然以vs为主 本人主页之前介绍过vs的安装即使用
接下来就正式进入正题
在学习c语言的时候 可能会遇到下面的情况
#include
int time = 0;
int main()
{
printf("%d \n", time);
return 0;
}
这段代码没有任何问题 但是下面这样写就会出问题
#include
#include
int time = 0;
int main()
{
printf("%d \n", time);
return 0;
}
time本来是自己定义的一个全局变量 但是在库里面是一个库函数 所以就会出现重定义错误
在c++中就有了命名空间namespac这个概念 来解决c语言的缺陷
定 义命名空间,需要使用到namespace关键字
//命名空间
namespace test
{
//命名空间可以定义变量、函数、结构体等...
int time = 0;
int Add(int x, int y)
{
return x + y;
}
struct student
{
int id;
char name[20];
};
}
namespace 后面跟命名空间的名字(自定义名称) 然后加{ }即可
命名空间的使用方式一共有三种 接下来一一介绍
::两个冒号就是c++的作用域限定符
int main()
{
test::time = 10;
printf("%d \n", test::time);
return 0;
}
使用上面命名空间中的time变量 test::time
空间名称加作用域限定符加变量名即可
在引用结构体的时候正确的语法是
struct test::student s1;
而不是
test::struct student s1;
为了避免每次使用变量都要加test::
可以用下面的方法
int main()
{
using test::time;
time = 10;
printf("%d", time);
return 0;
}
int main()
{
time = 10;
Add(6, 7);
struct student s1;
printf("%d", time);
return 0;
}
以上就是关于命名空间的知识了下面看一下c++的输出和输出
在c语言中 输入输出都需要指定数据类型 %d %f 等等
在c++的输出输出中就不用指定其数据类型 会自动识别类型
在c语言中使用输入输出函数都需要引用头文件stdio.h
在c++中std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中 需要包含头文件iostream
先看输出
#include
using namespace std;
int main()
{
cout << "Hello C++" << endl;
return 0;
}
cout endl 都是在std命名空间里面的需要引用std
cout 是c++标准输出对象
endl 是换行
<<流插入运算符可以自动识别数据类型
输入
#include
using namespace std;
int main()
{
int a = 0;
double b = 0;
char c = 0;
cin >> a;
cin >> b;
cin >> c;
cout << a << " " << b << " " << c << endl;
return 0;
}
cin标准输入对象
“>>” 是流提取运算符也可以自动识别数据类型
是指在声明或定义函数时为函数参数指定一个缺省值。在调用该函数时如果没有指定实参则采用形参的缺省值。否则使用实参指定值
例如
void Print(int a = 0)
{
cout << a << endl;
}
int main()
{
Print();//没有传实参 使用形参的值 打印0
Print(10);//指定实参的值 使用实参指定的值 打印10
return 0;
}
即函数全部形参都指定一个缺省值
例如
void Print(int a = 10, int b = 10, int c = 10)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
即函数部分形参指定缺省值
例如
void Print(int a, int b, int c = 10)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
缺省参数注意:
1.半缺省参数必须从右向左依次给 不能间隔着给
例如
//错误代码
void Print(int a, int b=10, int c)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
2.缺省参数不能在函数声明和定义中同时出现
//错误代码
//函数声明
void Print(int a = 0);
int main()
{
Print(1);
return 0;
}
//函数定义
void Print(int a = 0)
{
cout << a << endl;
}
3.缺省值必须时常量或者全局变量
c++允许在同一作用域中声明几个功能类似的同名函数。这些同名函数的形参列表的(参数个数\类型\类型顺序)不同。
//函数重载
//参数类型不同
int Add(int x, int y)
{
return x + y;
}
double Add(double x, double y)
{
return x + y;
}
int main()
{
Add(1, 1);
Add(1.1, 1.1);
cout << "Add(1, 1)=" << Add(1, 1) << endl;
cout << "Add(1.1, 1.1)=" << Add(1.1, 1.1) << endl;
return 0;
}
//参数个数不同
int Add(int x, int y)
{
return x + y;
}
int Add(int x, int y,int z)
{
return x + y + z;
}
int main()
{
Add(1,1);
Add(1,1,1);
cout << "Add(1, 1)=" << Add(1, 1) << endl;
cout << "Add(1,1,1)=" << Add(1,1,1) << endl;
return 0;
}
//参数顺序不同
void Fun(char a, int b)
{
cout << a << endl;
cout << b << endl;
}
void Fun(int b, char a)
{
cout << a << endl;
cout << b << endl;
}
int main()
{
Fun('a',10);
Fun(10,'a');
return 0;
}
引用不是新定义一个变量,而是给已经存在的变量取一个别名 因此编译器不会为引用变量开辟内存空间,和它引用的变量共用用一块空间
语法: 类型& 引用变量名 = 引用实体
int a = 10;
int& ra = a;//定义引用类型
int main()
{
int a = 10;
int& ra = a;//定义引用类型ra
cout << "a=" << a << endl;
cout << "ra=" << ra << endl;
ra++;//改变ra 实际上就是改变a
cout << "a=" << a << endl;
cout << "ra=" << ra << endl;
a++;//改变a ra也会改变
cout << "a=" << a << endl;
cout << "ra=" << ra << endl;
return 0;
}
1.引用的时候必须初始化
//错误写法
int a = 10; int& ra;
2.一个变量可以有多个引用
int a = 10; int& ra = a; int& rb = a; int& rc = a; int& rd = a;
ra rb rc rd 都是变量a的引用
3.引用一旦引用 在不能引用其他实体
int a = 10; int& ra = a; int c = 20; ra = c;
ra已经是a的引用 这是想让ra引用c是做不到的
const int a = 10;
//int& ra = a;//a本来是常变量 不能修改 而ra是整形 权限被放大了 语法不支持
const int& ra = a;//正确写法
int b = 10;
const int& rb = b;//b本来是可改的 而rb不能改 权限被缩小了 语法支持
//int& c = 10;//给常量取别名,常量不能被修改 权限放大 error
const int& rc = 10;//正确写法
double d = 3.14;
//int& rd = d;//类型不同 编译错误
const int& rd = d;//从double到int会存在隐式类型转换 类型转换会产生临时变量,临时变量具有常性所以支持
关于常引用:引用的权限不能放大但是可以缩小
在类型转换时(隐式类型转换/强制类型转换)会产生临变量 而临时变量具有常性下面这种写法也是可以的
int i = 1;
double j = i;//i-->j产生临时变量
const double& ri = i;
1.做参数
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
和指针类似 函数结束释放形参 也会改变实参
2.做返回值
int& Add(int a, int b)
{
int c = a + b;
return c;
}
函数内部创建普通局部变量 函数调用结束时布局变量也会被销毁 这种情况不能引用返回 否则结果不确定
int& Count()
{
static int s_n = 0;
s_n++;
return s_n;
}
返回的数据应该时static修饰的或者时malloc的或者时全局变量 不会随着函数调用结束而销毁的数据
在语法上 引用就是一个别名,没有独立空间,和引用实体共用一快空间
在底层实现上实际是有空间的 引用时按照指针的方法来实现的
我们一般只讨论语法
区别
1.引用概念上时一个变量的别名,指针时存储一个变量的地址
2.引用在使用时必须初始化,指针没有要求
3.引用初始化引用一个实体后,不能在引用其他实体。 指针可以在任何时候指向任何同类型实体
4.没有空引用 但是有空指针
5.sizeof中含义不同 引用的结果时引用实体类型的大小 指针的大小是固定的4/8
6.引用++即引用实体自增,指针++即指针向后移动一个类型的大小
7.没有多级引用 但是有多级指针
8.访问实体方式不同 引用编译器自己处理,指针需要进行解引用
9.引用比指针相对来说更安全
用inline修饰的函数叫做内联函数
比如
inline int Add(int x, int y)
{
return x + y;
}
内联函数编译器在编译c++时会在调用内联函数的地方进行展开,没有函数调用时建立的栈帧,内联函数提升程序的运行效率
从汇编代码可以看出 内联函数并没有去调用Add函数这个指令
在c++11中auto 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符
比如
auto a = 10;
auto b = 1.1;
auto c = 'c';
cout << typeid(a).name() << endl;//打印a的类型
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
结果分别是int double char
1.使用auto必须对其进行初始化
错误示范
auto d;
在编译阶段编译器需要根据初始化表达式来推导auto的类型。因此auto并非一种类型的声明,而是一个类型声明的占位符,在编译阶段时会将auto替换为实际的类型
2.auto与指针和引用结合使用
用auto声明指针类型时 auto和auto*没有任何区别 但是auto声明引用类型时必须加&(auto&)
#include
using namespace std;
int main()
{
int x = 10;
auto a = &x;
auto* pa = &x;
auto& ra = x;
cout << typeid(a).name() << endl;//结果为int*
cout << typeid(pa).name() << endl;//结果为int*
cout << typeid(ra).name() << endl;//结果为int
return 0;
}
3.在同一行定义多个变量
在同一行定义多个变量时 这些变量必须是同一类型 否则编译器会报错
比如
auto a = 10, b = 10;
auto c = 10, d = 1.1;//报错
1.auto不能做为函数的参数
void Test(auto x)
{}
2.auto不能直接用来定义数组
void Test()
{
auto arr[] = { 1,2,3,4,5 };
}
在之前的c语言或c++98中要遍历一个数组 需要按照下面的方式进行
void Test()
{
int arr[] = { 1,2,3,4,5 };
for (int i = 0; i < sizeof(arr)/sizeof(int); i++)
{
//....
}
}
在c++11中引入了基于范围的for循环 实现方法如下
for (auto e : arr)
{
//...
}
for循环条件由5两部分组成 第一部分是范围内用于迭代的变量 第二部分表示迭代的范围
会依次取数组中的值赋给e 自动判断结束 自动++向后走
和普通循环一样 也可以同continue和break
for循环的迭代范围必须是确定的
下面的例子就是错误的
void Test(int arr[])
{
for (auto e : arr)
{
//...
}
}
c++98中的指针空值
int* p = NULL;
int* p = 0;
NULL实际上是一个宏 在传统c头文件中可以看到下面的代码
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else /* __cplusplus */
#define NULL ((void *)0)
#endif /* __cplusplus */
#endif /* NULL */
可以看出 NULL可能会被定义成 0 或这是无类型指针(void*) 的常量 因此在定义空指针时 可能会遇到下面的问题
void test(int)
{
cout << "test(int)" << endl;
}
void test(int*)
{
cout << "test(int*)" << endl;
}
int main()
{
test(0);
test(NULL);
test((int*)NULL);
return 0;
}
程序本意时想通过test(NULL)调用指针版test(int*)函数 但是由于NULL被定义为0 最终调用的时test(int)函数
在C++98中字面常量0,既可以是一个整型数字,也可以是无类型的指针(void*)常量,但编译器默认情况下将其看成是一个整型常量,如果要将其按照指针方式来使用,必须对其进行强制转换。
c++11的空值指针
对于c++98中的问题 c++11引入关键字nullptr
int* p = nullprt;
1.在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为关键字引入的。
2.在C++11中,sizeof(nullptr)与sizeof((void*)0)所占的字节数相同。
3.为了提高代码的健壮性,在后序表示指针空值时建议最好使用nullptr。