C++进阶

文章目录

宏指令

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;
}

解决方案:

  1. 添加枚举类,明确指定哪一个枚举,解决问题2
  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(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值