C++基础
因为工作需求学习C++,内容正在不断更新,内容主要来自于网上的视频课
C++是C的超集
C++在C上添加了面向对象编程和泛型编程的思想
一、语法基础
1.1 Hello world实现
1.1.1 命名空间:
using namespace std; 使用命名空间std,理解:打开一个std的房间,房间里有cout等,打开一次代码就可以一直用
也可以通过std::cout<<“hello world”<<endl; 的方式实现,但是每次需要std都需要打开一下房子
1.1.2 阻塞功能
system(“pause”); 让命令窗口保留
return EXIT_SUCCESS 表示代码成功离开,返回正常退出
1.1.3 面向对象三大特性
- 封装
把客观对象抽象成类 - 继承
父子对象 - 多态
一个接口多种方法
1.2 双冒号作用域运算符
在函数里调用全局变量的时候可以使用::[变量名]的方式调用
::前面如果有std或者什么范围,就相当于到对应的范围找
1.3 命名空间的使用
在game1.h文件中
namespace LOL
{
void goAtk();
}
在game1.cpp文件中
#include "game1.h"
void LOL::goAtk()
{
cout << "LOL攻击实现" <<endl;
}
在本文件或者其他文件运行的时候:
int main(){
LOL::goAtk();
}
- 命名空间内可以放函数、变量、结构体、类
但是必须定义在全局作用域下,不能在函数内部 - 命名空间可以嵌套命名空间
namespace A
{
int a = 20;
namespace B
{
int a = 10;
}
}
int main()
{
//输出B下的a
cout << A::B::a << endl;
}
//输出为10
- 命名空间是开放的可以在后续程序继续向里面放内容,会自动合并
namespace A
{
int a = 20;
}
namespace A
{
int b = 30;
}
//这时候A里面a和b都有
- 无名命名空间,相当于static int C; static int D;也就是只能在当前文件内使用
namespace {
int C = 0;
int D = 0;
}
- 命名空间可以起别名
namespace veryLong{
int C = 0;
int D = 0;
}
namespace veryshort = veryLong; //即可完成起别名
1.4 using声明和using编译指令
二义性:
避免如下图的情况

1.5 C++对C语言的增强
- 全局变量的检测增强
以下代码在C中可以跑通,但是在C++不可以
int a;
int a = 10;
- 函数检测增强
以下代码在C中可以跑通,但是在C++不可以(没有参数类型和返回值,参数数量不对)
get(10, 10, 10)
int get(w, h){
}
- 类型转换检测增强
以下代码在C中可以跑通,但是在C++不可以(需要改成char *p = (char *)malloc(sizeof(64));)
void test(){
char *p = malloc(sizeof(64)); //malloc返回值是void*
}
- struct增强
C中struct不能加函数,C++中可以(void plusAge()😉
struct Person
{
int Age;
// void plusAge();
}
定义结构体的时候,C++可以简洁一点
C:
struct Person p;
C++:
Person p;
-
bool类型增强
C语言中没有bool类型
C++有
非0的bool类型值都默认转为1
所以不论如何赋值,bool只能存0/1 -
三目运算符增强
常见情况:
int a = 10;
int b = 20;
c = a > b ? a :b;
// c = 20
C++特有允许情况:
int a = 10;
int b = 20;
(a > b ? a : b) = 100;
// a = 10, b = 100, 因为C++的三目运算符返回的是变量(这里是b),C返回的是数值
- const增强
- C语言的const是伪常量
const int a = 10;
// a = 100; C语言这种方式不可以,不能对const直接改值
// 但是C语言用下面指针的方式依旧可以修改a的值,因为编译器是分配内存的方式定义数据
int *p = (int *)&a;
*p = 200;
// a = 200
C++通过上面的方式可以运行,但是a的值没有被改变,a依旧是10
因为C++不是通过分配内存的方式定义数据,利用类似下面的表格存放数据
新开指针的时候会分配一个临时内存

-
C语言中const默认是外部链接,C++中默认是内部链接
意思就是说C++中定义的const int a,其他文件调用该文件的时候,不能直接通过extern调用a,定义的时候需要extern const int a; -
分配内存情况
如何防止开辟临时内存导致const无法更改呢
利用普通变量初始化const的变量
int a = 10;
const int b = a;//会分配内存
int * p = (int *) &b;
*p = 1000;
cout << b <<endl;
//输出为1000
- 自定义数据类型(比如结构体)
const Person p1;
//p1.m_Name = "aaa"; 这种方式不可以改const修饰的结构体
Person *p = (Person*)&p1;
p->m_Name = "元歌";
(*p).m_Age = 18;
//利用如此的两行就可以全部修改出来
所以C++中,const修饰的变量是可以用于初始化数组的,C中则不可以
1.6 尽量用const代替define
对于下面的语句,MAX从没被编译器看到过,因为在预处理阶段所有的MAX都被自动替换为1024了,所以相关的报错不会提到MAX,而都是1024
define的数没有类型,无法类型检测
const能完全替换define的功能
difine的变量没有作用域,自始而终都存活(虽然可以用#undef MAX 解决)
#define MAX 2014
1.7 引用,意思就是起别名
1.7.1 引用的知识点
- 语法:
int &b = a;//引用
int b = &a;//取地址
b和a的作用就是等同的了
-
引用必须初始化
int &a;这样的代码是不可以的 -
引用初始化之后不可修改
int &b = a;//引用
b = c;//这行不可执行,报错
- 对数组建立引用
int arr[10];
int (&pArr)[10] = arr;
- 引用必须引一个变量等合法空间
int &a = NULL;
int &a = 10; 这两个都是不可以的
- 引用不能在外部引局部变量
(static int a可以解决下面的问题)

1.7.2 引用的本质
引用在C++内部的实现是一个指针常量

1.7.3 指针的引用
可以有效避免二级指针的使用

1.7.4 常量的引用
int &a = 10; 不合法的引用
const int &a = 10; 合法的引用,因为在加入const之后,编译器的处理方式是 int tmp = 10;const int &a = tmp;
int *p = (int *)&a;
*p = 1000;
在上面两行的操作之后,a的值就变成1000了
1.8 参数的传递方式
值传递
地址传递
引用传递
二、函数和封装
2.1 对象中函数的设计
一个简单的求圆周长的程序
Public能够让对象的东西可以在外部访问
#include <iostream>
using namespace std;
const double pi = 3.14;
//设计一个圆的类然后求周长
class Circle{
public ://公共权限
int r;//成员属性
double cal_c(){
return 2 * pi * r;
}
void set_r(int new_r){
r = new_r;
}
};
int main(){
//通过类创建一个圆
Circle c1; //圆,对象
c1.r = 10;
c1.set_r(5);
cout << c1.cal_c()<<endl;
return 0;
}
2.2 内联函数
宏函数:
可以将一个或者多个变量自动替换成一个规定好的表达式,不需要对栈进行操作,提高运行效率和内存空间
宏函数的缺陷:
下面程序输出的结果是410,因为Myadd(10 ,20) * 20在编译器里运行的时候,被编译成10 + 20 * 20
所以要改成# define Myadd(x,y) (x+y)
# define Myadd(x,y) x+y
void test01()
{
int ret = Myadd(10, 20) * 20; //预期结果 600
cout << "ret = " << ret << endl;
}
int main(){
test01();
return 0;
}
但是封装好的函数一般就不会出现这种问题,所以提出了内联函数实现相同的功能代替define
inline int Myadd(int x,int y)
{
return x + y;
}
加上关键字,编译器不一定就按照内联函数执行,只是一个建议,具体情况编译器自己可以判断
内联函数的注意事项(和普通函数的区别)
- 不能有循环语句,开销大默认不是内联
- 不能有太多条件判断
- 不能过于庞大
- 不能对函数进行取址(因为内联函数是个表达式替换,没有实际地址)
2.3 函数的默认参数
不传参的情况下,函数自己可以用提前定义好的参数值
void test(int a = 10, double b = 2.24){
...
}
注意事项:
如果有一个位置有了默认参数,那么右边的所有传参都需要有默认参数
void test(int a = 10, int ,b) 这样是不可以的
2.4 函数的占位参数
如果有占位参数,函数调用的时候必须提供这个参数,但是用不到
出现的几率很小
void func(int a , int){}
finc(4, 3)
函数的声明和实现最多只有一个地方有默认参数
像下面这样实现是可以的
void func(int a = 10, int b = 10);
void func(int a, int b){}
2.5 函数的重载
2.5.1 内容
函数名称可以重复,但是参数个数/类型/顺序不同,在同一个作用域中就叫做函数重载。
//以下的函数定义可以同时存在于一个作用域中
void func(){}
void func(int a){}
void func(const int a){} // 因为const开辟临时空间,所以可以这么写
void func(double a){}
void func(double a, int b){}
void func(int a, double b){}
...
但是函数的返回值不能作为函数的重载条件
下面这个情况是不可以的,调用的时候会编译错误
int func(){return 3;}
double func(){return 3.14;}
重载中使用默认参数也会产生二义性问题,下面的两个情况就发生了问题
void func(int a , int b = 10){}
void func(int a){}
2.5.2 重载的原理

2.6 extern_C
在C++中,函数重载之后,编译器会把这个函数的名称偷偷改变
通过#include调用h文件方法的时候要在程序前面加上
extern “c” void func(); //func是要调用的函数名
#ifdef __cplusplus
extern “C”
解决的问题就是在C++中调用C的代码
2.7 private, public, protected
2.7.1 三者功能
C语言封装的属性和行为分开处理了,导致繁琐并且有可能能产生功能的误用
C++因为支持了结构体内部函数,所以模块化更强,对象和类型之间的关系更紧密
struct和class是一个意思,唯一的不同在于,struct是public,class默认是private
私有权限代表了对应的内容在类的外部不可访问,只能在类的内部调用,子类也不能访问,在前面涉及过
使用pulic :可以将后面的内容变成公共的
protected: 用于保护权限,类内部和子类可以访问,类外部不可以访问
默认的权限是private:(写不写都行,建议把private的内容标注上)
2.7.2 建议将成员属性设置成私有的
私有的属性保护性比较强,虽然不能直接更改,但是可以在类的内部添加Public的方法,通过调用方法来读写成员内部的属性。
而且通过方法修改值,可以在处理过程中添加判断语句等,能够更好地满足对应的业务,防止对数据胡乱更改
三、面向对象设计
3.1 构造函数和析构函数
由编译器自己生成,一开始都是空的
构造函数在构造时候被自动调用,析构函数在清理空间被自动调用
构造函数可以被重构



680

被折叠的 条评论
为什么被折叠?



