文章目录
宏指令
1、 常用宏
__STDC_HOSTED__ //是否包含完整的标准C库 1/0
__FUNCTION__ //函数名
__FILE__ //当前物理文件名
__LINE__ //当前行数
__DATE__ //当前日期 "MMM DD YYYY" 格式
__TIME__ //当前时间 "HH:MM:SS" 格式
__cplusplus //支持 c++03中值为199711L,c++11中值为201103L。版本越高只会越大
1、用法
#include<iostream>
using namespace std;
int main() {
cout << __STDC_HOSTED__ << endl;
cout << "当前函数" << __FUNCTION__ << endl;
cout << "当前物理文件名" << __FILE__ << endl;
cout << "当前行数" << __LINE__ << endl;
cout << "当前日期" << __DATE__ << endl;
cout << "当前时间" << __TIME__ << endl;
//支持c++03中值为199711L,c++11中值为201103L。版本越高只会越大
cout << "当前是否是C++ " << __cplusplus << endl;
return 0;
}
/*
1
当前函数main
当前物理文件名e:\studyfile\vsfile\c++进阶\c++进阶\c++进阶.cpp
当前行数9
当前日期Feb 24 2021
当前时间11:37:17
当前是否是C++ 199711
*/
2、常用宏处理(预处理)
#include //包含头文件
#define //定义宏
#undef //取消已定义的宏
#if //如果条件为真,编译下面代码
#else //配合#if使用,否则
#elif //否则如果
#endif //结束#if
#error //编译期间遇到这个定义就会报错,这样可以用来判断宏定义中条件判断是否错误
#pragma once //该头文件只编译一次
//#pragma once 等同于以下代码
#ifndef THIS_HEADER
#define THIS_HEADER
代码
#endif
头文件重复包含
1. 解决方法1
#pragma once
//优点:简单,方便
//缺点:不通用,有一些编译器不支持
2. 解决方法2
//A.h
#ifndef __A__
#define __A__
//ifndef:if not define 如果没定义
//ifdef:if define 如果定义了
class A{
};
#endif
//B.h
#ifndef __B__
#define __B__
#include "A.h"
class B:public A{
};
#endif
//main.c
#include "A.h"
#include "B.h"
int main(){
A a;
B b;
}
C/C++ 混合编程
1、C/C++混合编程
1. 为什么 C 和 C++需要混合编程
因为正常情况下, C++编译器在编译.cpp文件时生成的函数名和全局变量与C编译器在编译.c文件时生成的函数名和全局变量是不一样。
2. C++和C之间的函数为什么无法直接互相调用?
C++ 编译有一个函数名重整,和c编译后生成的函数不同。
c语言中,函数a和全局变量b会被编译成 _a 和 _b 这种格式
c++中,函数run和全局变量var会被编译成类似 ?run@@YAXXZ 和 ?var@@3HA 这种格式
3. 为什么C++要 “函数名重整”?
C++ 要支持,函数名重载机制
4. 怎么解决这个问题?
解决方案
//告诉C++编译器,内部的内容全以C的结构来编译
extern "C"{
函数声明;}
让 C++ 生成 C 格式的最终的函数
5. 怎么将该解决方案应用到头文件中?
(让 C 语言 和 C++ 源代码都可以自如包含盖头文件不出错)
使用 #include ______cplusplus 块来控制一些只应该出现在 C++ 中的内容。__cplusplus是C++编译器内置的宏。
将 extern “C” 放入头文件中会导致这句代码被处理到 .c文件中从而编译失败。让 .c 文件中,extern部分不参与编译,让 .cpp 中extern 部分参与编译
//getPi.h
#pragma once
#ifdef __aplusplus //__aplusplus:在c++环境下为1,在c环境下为0
extern "C"{
#endif
float getPi();
#ifdef __cplusplus
}
#endif
空指针 nullptr
以往我们用 NULL 代表空指针,它实质上是个为 0 的 int 值。所以在以下代码会产生歧义
void f(int){
} //1
void f(int*){
} //2
int main(){
f(NULL); //默认调用1
return 0;
}
为此C++ 11 新增类型 nullptr_t,它只有一个值 nullptr。是个关键字,代表空指针。
void f(int){} //1
void f(int*){} //2
int main(){
f(nullptr); //调用2
return 0;
}
using 新的类型名重定义语法
//using 也可以起到重定义类型名
using aab = int;
typedef int aac;
int main(){
aab c = 4; //等价于 int = 4;
aac d = 25; //等价于 int = 25;
return 0;
}
using 和 typedef 都能重定义类型名,唯一的区别是 using 能用在模板上,而 typedef 却不能
template<class T> using v = std::vector<T>;
typedef template<class T> std::vector<T> aac; //报错
int main(){
v<int> arr;
return 0;
}
auto 类型名推导
auto var1 = 25; //auto 是值类型
vector<vector<int>> arr1;
auto arr2 = arr1; //auto 是值类型
auto& arr3 = arr1; //auto& 就是引用类型
auto* arr4 = &arr1; //auto 是指针类型
const相关 枚举
1、const 相关
1. const 指针变量和变量指针
// c文件
//const 修饰 变量 该变量是 只读
//p1,p2都指向const int类型变量
int const *p1 //p1指向一个const int 类型变量
*p1 = 3; //×
const int* p2 //p2指向一个const int 类型变量
*p2 = 4 //×
//p3 是const 指针
int* const p3 //p3指向int类型变量,p3本身是一个只读变量
(*p3) = 5; //√
p3 = 6; //×
2. .C文件改变 const 变量值
//c文件中
int main(){
const int c = 25;
printf("地址为%p,值为%d\n",&c,c);
//c 这个标识符 对应了内存中4个字节的空间。但不可以通过c来修改这个空间
int* p = (&c); //当成int类型的地址
(*p) = 77;
printf("地址为%p,值为%d\n",&c,c);
return 0;
}
/*
地址为0073F91C,值为25
地址为0073F91C,值为77
*/
3. C++ 中,对于 const 变量初始化为自变量时,会有类似于宏定义的优化
int main(){
const int c = 55; //C++ 变量关于 const 变量初始化为字面量有一个优化
cout << "地址:" << &c << "\t值:" << c << endl;
int arr[c];
int* pc = (int*)c; //&c 是一个 const int* 类型变量
cout << "pc地址:" << &pc << "\tpc值:" << pc << "\t*pc值"<< *pc << endl;
*pc = 77;
cout << "修改c对应内存后" << endl;
cout << "地址:" << &c << "\t值:" << c << endl;
cout << "pc地址:" << &pc << "\tpc值:" << pc << "\t*pc值" << *pc << endl;
return 0;
}
/*
地址:00F8F77C 值:55
pc地址:00F8F68C pc值:00F8F77C *pc值:55
修改c对应内存后
地址:00F8F77C 值:55
pc地址:00F8F68C pc值:00F8F77C *pc值:77
*/
这里解释一下 c++ 中 const 会有类似宏定义的优化,如上面代码,利用指针想修改 c 的值,但 c 的值仍然是 55(c语言中c会被修改为77)。这是为什么呢?
因为在 C++ 中 const 一个变量约等于是将这个变量宏定义为一个常量,如这里的
const int c = 55; //约等于 #define c 55
所以这里的c与55已经绑定了,但是它又是灵活的 :&c 是 c 的地址,而不是 &55
4. const在常函数中的应用
class NODE{
int value;
public:
NODE(){
}
NODE(const NODE& o){
//拷贝构造函数的功能,是拷贝出另一个对象。
}
void run(){
//非常函数
this->value = 25; //this类型:NODE *const this
}
void crun() const{
//常函数
this->value = 333; //this类型:const NODE *const this 报错,不能修改
}
void drun() const{
//常函数中修改成员变量
((NODE*)this)->value = 33; //this类型由 const NODE* 转化为 NODE* 类型
}
}
非常成员函数中:this 指针是: T* const this
常成员函数中:this 指针是: const T* const this
面试题:常函数底层如何实现?
this 指针变成了 const 指针类型,再指向一个const 变量类型。
面试题:如何在常函数中修改成员变量?
将 this 指针转成非 const 指针,用新指针修改成员变量:T* p = (T*)this
5. constexpr
作用:检查赋值和函数是否是常量,起到检查作用。
若赋值的右侧是个变量而不是常量,则会报错
若函数的返回值是个常量,则这个函数可以当成常量使用。
int gv = 66;
constexpr int getSize1(){
return gv;
}
constexpr int getSize2(){
return 5;
}
int getSize3(){
return 5;
}
void constExprTest(){
constexpr int d = 5; //√
int c = 1;
constexpr int d2 = c; //×
int arr1[getSize1()]; //× gv不是常量,报错
int arr2[getSize2()]; //√ 加了 constexpr ,会检查是否是常量,若是则当常量用。
int arr3[getSize3()]; //× 未加 constexpr ,不会检查是否是常量,直接报错
}
强枚举类型
枚举类
class 王者荣耀英雄{
int state;
};
enum 王者荣耀英雄 {
静止,移动,攻击前摇,攻击后摇};//定义了一个枚举类型
int main(){
王者荣耀英雄 state;
state = 静止;
return 0;
}
C++11 中,引入了枚举类来解决C语言中 枚举的一些问题
那么 C 语言中的枚举有哪些问题呢?
1. C语言中的枚举变量一定是一个4字节的 int 类型
2. C语言中两个不同枚举,可能冲突。例如在两个枚举中有共同的枚举值,则程序无法判断用哪一个枚举的枚举值。
C++ 解决方案:
enum class 王者英雄:long long{
//每次使用内部的状态都必须加作用域名
静止,移动,攻击前摇,攻击后摇
};
int main(){
王者英雄 a;
a = 王者英雄::攻击前摇; //使用时要加作用域
return 0;
}
解决方案:
- 添加枚举类,明确指定哪一个枚举,解决问题2
- 指定类型,解决问题1
整形提升规则
所有表达式和变量计算时,如果计算的操作数小于4个字节,就转换为4个字节来计算。
char 和 short 进行计算时,会直接转为 int计算
32位系统,底层的 CPU 操作数据的基本单位是4个字节,无法单独操作 char 和 short 进行计算时,会直接转为 int 计算。
32位系统,底层的 CPU 操作数据的基本单位是 4 个字节,无法单独操作 char 和 short,所以需要转换为 int
同类型的有符号和无符号运算,会转换为无符号变量在运算。
原始字符串(Raw String)
出现原因
一些字符串在C++的环境下会有其他含义(转义,换行),而不能保留原有含义。
格式
R"自定义结束符( 字符串内容)自定义结束符";
注意:自定义结束符的存在是为了确定最后在哪结束。不能和字符串内容重复。
实例
const char* html = R"xyz(hello world)xyz";
自定义后缀
C++ 自带后缀
auto a = 25l; //long类型 l 就是long的后缀
auto b = 25.f; //float类型 f 就是float后缀
自定义后缀是程序员自己定义的后缀,目的是增加代码可读性,提高效率
int operator"" square(unsigned long long n){
return n*n;
}
int main(){
cout << 2square << endl; //等价于 2*2
}
/*
参数类型:
unsigned long long
char
wchar_t
char16_t
char32_t
*/
3、可执行体系列
1、函数指针
void f(){
}
void functionPointer(){
void (*p)() = &f; //获得一个函数指针,指向了f函数
//函数指针就是4个字节的小指针。说白了不是对象
}
2、成员函数指针
//成员函数的函数指针
class ND{
public:
void run(){
cout << "调用了成员函数" << endl;
}
static void sr(){
cout << "调用静态成员函数" << endl;
}
}
void memberFunctionPointer(){
void (ND::*f)() = &ND::run;
//少了个 ND::
void (*f2)() = &ND::sr; //静态函数,就可以用全局函数的函数指针指向
ND n;
ND* pn = &n;
(n.*f)();
(pn->*f)();
}
int main(