c++随笔-4

define

#include <cxxabi.h> // 包含 __cxa_demangle 函数,将类型转换成编码人员可阅读的类型

void DefineFunc() {
    // 1. 定义常量,预处理阶段会用最后边的内容替换宏定义的名称
#define MY_PI 3.1415926  // # 此处宏定义常量 3.1415926 的名称为 MY_PI
    std::cout.precision(7);    // 设置小数点后打印精度
    std::cout.setf( std::ios::fixed, std::ios::floatfield );
    std::cout << "MY_PI value: " << MY_PI << std::endl;
    printf("MY_PI value: %.7f\n", MY_PI);
    std::cout.unsetf(std::ios::floatfield);
    // 2. 宏定义函数
#define ADD(a, b) a + b
    int a = 10;
    int b = 12;
    std::cout << "ADD value: " << ADD(a, b) << std::endl;
    // 以上这个宏定义函数有点问题,因为宏定义只是进行文本替换,不做类型检查
    std::cout << "ADD value: " << a * ADD(a, b) * a << std::endl;
    // 预处理后 a * a + b * a,没有期望中的 a * (a + b) * a
#define ADD2(a, b) (a + b)  // 加括号解决上述问题,所以写宏定义函数时,推荐每层都加括号确定优先级
    std::cout << "ADD2 value: " << a * ADD2(a, b) * a << std::endl;

    // 3. define 中的特殊操作符有#、##、…、__VA_ARGS__
#define A(x) #x   // 将x转换为字符串
    std::cout << "A(3.1415926) value: " << A(3.1415926) << std::endl;
    std::cout << abi::__cxa_demangle(typeid(decltype(A(3.1415926))).name(), 0, 0, 0) << std::endl;
    std::cout.setf(std::ios::boolalpha);
    std::cout << (typeid(decltype(A(3.1415926))).name() == typeid (char [10]).name()) << std::endl;
    std::cout.unsetf(std::ios::boolalpha);
#define B(n) x##n      // 拼接字符串
    int B(1) = 1;  // int x1 = 1;
    // 可变参数 ... 和 __VA_ARGS__
#define C(format, ...) (printf(format, __VA_ARGS__))   // 不允许省略可变参数
#define D(format, ...) (printf(format, ##__VA_ARGS__)) // 允许省略可变参数
#define E(format, arg...) (printf(format, arg))        // 不允许省略可变参数
#define F(format, arg...) (printf(format, ##arg))      // 允许省略可变参数
    C("-> %s, %d, %s\n", "sss", 23, "sss");
    D("-> %s, %d, %s\n");   // 此处省略可变参数
    E("-> %s, %d, %s\n", "sss", 23, "sss");
    F("-> %s, %d, %s\n", "sss", 23, "sss");

    // 4. 多行定义
#define DELETE_PTR(ptr) do { \
    if (ptr != nullptr) { \
    delete [] ptr; \
    ptr = nullptr; \
    } \
    } while (0);  // 释放指针数组
    int *p = new int[2];
    DELETE_PTR(p);
    std::cout.setf(std::ios::boolalpha);
    std::cout << "p == nullptr: " << (p == nullptr) << std::endl;
    std::cout.unsetf(std::ios::boolalpha);

    // 5. 条件编译,此处和当前文件有冲突,为编译通过才将此注释
    // #define _Win32    // 根据不同平台添加对应头文件,
    // #include <windows.h>
    // #else   // 可省略
    // #include <sys/epoll.h>
    // #endif

    // 6. 解决不同源文件中对同一个头文件多次重复包含,此处和当前文件有冲突,为编译通过才将此注释
    // #ifndef TEST_DEMO_H_   // 不同文件一般此定义不同,也必须不能相同
    // #define TEST_DEMO_H_
    // #include <iostream>
    // #endif
    // 7. 宏取消
#define G 10000
    std::cout << G << std::endl;
#undef G
    // std::cout << "G value: " << G << std::endl;  // 此处编译报错,因为G已经取消
}

输出结果:
MY_PI value: 3.1415926
MY_PI value: 3.1415926
ADD value: 22
ADD value: 220
ADD2 value: 2200
A(3.1415926) value: 3.1415926
char [10]
true
-> sss, 23, sss
-> (null), -891725504,
-> sss, 23, sss
-> sss, 23, sss
p == nullptr: true
G value: 10000

  • 宏定义形参是标识符,宏调用中实参可以是表达式
  • 带参数的宏定义中,书写时宏定义名称和形参之间不能出现空格
  • 若宏定义存在形参,实际上是不分配内存单元,所以不需要给形参写明类型

typedef

void TypedefFunc() {
    typedef int INT;   // 定义 int 的别名
    INT a = 10;
    typedef char * CHAR_PTR;  // 定义 char * 的别名
    CHAR_PTR b,c;  // 此处 char *b,char *c
    std::cout.setf(std::ios::boolalpha);
    std::cout << "typeid (char *) == typeid (b) : " << (typeid (char *) == typeid (b)) << std::endl;
    std::cout << "typeid (char *) == typeid (c) : " << (typeid (char *) == typeid (c)) << std::endl;
    std::cout.unsetf(std::ios::boolalpha);
#define CHAR_PTR2 char *
    CHAR_PTR2 d, e;  // 此处 char *d,char e,注意 define 和 typedef 这种方式定义使用的区别
    std::cout.setf(std::ios::boolalpha);
    std::cout << "typeid (char *) == typeid (d) : " << (typeid (char *) == typeid (d)) << std::endl;
    std::cout << "typeid (char *) == typeid (e) : " << (typeid (char *) == typeid (e)) << std::endl;
    std::cout << "typeid (char) == typeid (e) : " << (typeid (char) == typeid (e)) << std::endl;
    std::cout.unsetf(std::ios::boolalpha);

    typedef char ARRAY[2];  // 定义数组类型 char [2],注意数组也是一种类型
    ARRAY arr;
    std::cout.setf(std::ios::boolalpha);
    std::cout << "typeid (char [2]) == typeid (arr) : " << (typeid (char [2]) == typeid (arr)) << std::endl;
    std::cout.unsetf(std::ios::boolalpha);

    typedef void (*FUNC)(int);  // 定义一个形参,没有返回值的函数指针(回调函数多定义函数指针形式)
    FUNC func;
    // func = Demo;   // 例如存在一个函数,void Demo(int len){},就可使用 func = Demo,函数的名字是函数入口地址

    typedef struct Stu {   // 定义结构体别名
        int id;
    }STU;
    STU t;
    // 从c++11开始提供 using 关键字,功能更强大,可做的事情更多,其中就可代替 typedef
    using FLOAT = float;  // 定义 float 别名
    FLOAT f;
}

输出结果:
typeid (char *) == typeid (b) : true
typeid (char *) == typeid © : true
typeid (char *) == typeid (d) : true
typeid (char *) == typeid (e) : false
typeid (char) == typeid (e) : true
typeid (char [2]) == typeid (arr) : true

  • typedef 多用于定义别名,通俗来说就是给已有类型在起个新名字
  • c++11开始提供了功能更多的 using 关键字可代替 typedef

const

void ConstFunc() {
    // 1. const 修饰变量,位置置于变量之前即可,修饰后相当于常量,值不可被修改
    const int a = 10;  // a 和 b 写法没有区别
    int const b = 10;
    decltype (a) c = 20;  // 推导出 c 类型 const int
    // a = 30;  // 不允许修改
    // b = 30;
    // c = 30;
#define VAL 123
    const int d = 123; // 定义常量,代替宏定义写法,提高效率
    // const 修饰类对象时,此对象只能调用有 const 修饰的类成员函数
    // const 修饰类型成员变量时,只能使用初始化列表进行初始换,也可以在定义的时候直接用常量或枚举初始化
    /* class A {
     * public:
     * A() : a(1){}   // 在构造函数中使用初始化列表进行赋值初始化
     *
     * private:
     * const int a = 1;  // 直接初始化
     * };
    */

    // 2. const 修饰指针,判断时去掉类型,看 const 修饰的是指针还是指针指向的地址
    int x = 10;
    int y = 20;
    const int *e = &x;   // const 修饰指针指向的地址,即指针指向的地址可变,不能通过指针间接修改(常量指针)
    // *e = 20;    // error,不能通过指针改变此变量的值
    e = &y;        // 指针可以重新指向一个地址
    int const *f = &x;   // 这种写法和上边效果一样
    // *f = 20;    // error
    e = &y;        // ok
    int* const g = &x;   // const 修饰指针,指针一旦指定不可改变指向内容,可通过指针间接修改值
    // g = &y;     // error,指针指向的地址不可被修改
    *g = 20;       // ok,可以通过指针间接修改修改值
    const int* const h = &x;   // 第一个 const 修饰指针指向的地址,第二个 const 修饰指针
    // h = &y;     // error,指针指向的地址不可被修改
    // *h = 20;    // error,不能通过指针间接修改修改值

    // 3. const 修饰函数,写在函数后边,如 int Add(int a, int b) const{}
    // 修饰类成员函数时,其实修饰的是 this 指针,不能通过指针修改类的成员变量值

    // 4. 修饰函数形参时,初始化后表示函数中此参数不可修改,只能读取,一般提高性能写法如下 int Add(const std::string &a);
    // 不推荐修饰函数返回值,const int Add() {return 3;},编译器会提示警告信息

    // 若想修改 const 值,c语言中通过指针间接可修改,c++中可通过强制去除 const
    // 定义时使用 mutable 修饰(使用与类成员变量)
    // mutable int ss = 3; void Add() const { ss++;}    // OK
}
const int Add3() { return 3;}

int Add(int a, int b) const { return a + b; }
  • 使用 const 修饰的变量可看做常量,值不可被修改

c++引用

void ReferenceFunc() {
    int a = 10;
    int &ra = a;   // 引用基本语法,定义时必须初始化,引用内存的别名

    std::cout << std::dec << "a value : " << a << std::endl;
    std::cout << std::hex << "a address : 0x" << &a << std::endl;
    std::cout << std::dec << "ra value : " << ra << std::endl;
    std::cout << std::hex << "ra address : 0x" << &ra << std::endl;
    ra = 20;   //  通过引用修改变量值,指针需要解引用 *p = 20;
    std::cout << std::dec << "a value : " << a << std::endl;
    std::cout << std::hex << "ra address : 0x" << &ra << std::endl;
    // 引用地址上层无法看到,在编译时底层使用 const int * 实现(常量指针)
    // 引用使用在函数形参中,void Swap(int &a, int &b)
    int c = 10;
    int d = 20;
    std::cout << std::dec << "c value : " << c << std::endl;
    std::cout << std::dec << "d value : " << d << std::endl;
    Swap(c, d);
    std::cout << std::dec << "c swap value : " << c << std::endl;
    std::cout << std::dec << "d swap value : " << d << std::endl;

    // 可以定义指针的引用,不能定义引用的引用
    int *p = &c;
    int* &rp = p;
    // int && ra2 = ra;
}

void Swap(int &a, int &b) {     // 两值互换,参数以引用方式传递
    a = a + b;
    b = a - b;
    a = a - b;
}

输出结果:
a value : 10
a address : 0x0x65fd7c
ra value : 10
ra address : 0x0x65fd7c
a value : 20
ra address : 0x0x65fd7c
c value : 10
d value : 20
c swap value : 20
d swap value : 10

  • c语言有指针,c++兼容c语言的同时,添加了OOP、GP等特性,引用也是c++特性
  • 注意引用和指针使用的的区别

函数

void FunctionFunc() {
    // 函数语法
    // void        Func      (int a)         {}
    // 函数返回值   函数名称   [函数形式参数]   函数实现,其中形式参数可有可无,形参变量名称不使用也可不写

    // 函数形式参数不是引用或指针时,实际调用时一般都是对实际传入的参数拷贝一份在函数中使用,不会影响到实际变量
    // 函数形式参数是引用或指针时,实际调用时实际传入的是实际参数的地址,所以函数中对此地址的操作会影响到实际变量
    int a = 10;
    int b = 20;
    std::cout << std::dec << "a value : " << a << std::endl;
    std::cout << std::dec << "b value : " << b << std::endl;
    std::cout << "Swap1(int a, int b) : " << std::endl;
    Swap1(a, b);      // 按值传递
    std::cout << std::dec << "Swap1 a value : " << a << std::endl;
    std::cout << std::dec << "Swap1 b value : " << b << std::endl;
    std::cout << "Swap2(int &a, int &b) : " << std::endl;
    Swap2(a, b);      // 按指针传递
    std::cout << std::dec << "Swap2 a value : " << a << std::endl;
    std::cout << std::dec << "Swap2 b value : " << b << std::endl;
    std::cout << "Swap3(int *a, int *b) : " << std::endl;
    Swap3(&a, &b);    // 按指针传递
    std::cout << std::dec << "Swap3 a value : " << a << std::endl;
    std::cout << std::dec << "Swap3 b value : " << b << std::endl;
    std::cout.setf(std::ios::hex);
    std::cout << std::dec << "Swap3 address : 0x" << &CDay1::Swap2 << std::endl;
    std::cout.unsetf(std::ios::hex);

    // 一维数组做函数形参
    auto Print1 = [](int *arr, int len) {    // 指针做形式参数
        std::cout << abi::__cxa_demangle(typeid(arr).name(), 0, 0, 0) << std::endl;
        for (int i = 0; i < len; ++i) {
            std::cout << "arr[" << i << "] = " << arr[i] << "\t";
        }
        std::cout << std::endl;
        arr[0] = 10;  // 此处值改变会影响函数外边的数组中对应的元素值
    };
    auto Print2 = [](int arr[], int len) {    // int [] 做形式参数,退化为指针
        std::cout << abi::__cxa_demangle(typeid(arr).name(), 0, 0, 0) << std::endl;
        for (int i = 0; i < len; ++i) {
            std::cout << "arr[" << i << "] = " << arr[i] << "\t";
        }
        std::cout << std::endl;
        arr[0] = 20;  // 此处值改变会影响函数外边的数组中对应的元素值
    };
    auto Print3 = [](int arr[4], int len) {    // int [4] 做形式参数,退化为指针
        std::cout << abi::__cxa_demangle(typeid(arr).name(), 0, 0, 0) << std::endl;
        for (int i = 0; i < len; ++i) {
            std::cout << "arr[" << i << "] = " << arr[i] << "\t";
        }
        std::cout << std::endl;
        arr[0] = 30;  // 此处值改变会影响函数外边的数组中对应的元素值
    };

    int arr[4] = {1, 2, 3, 4};
    std::cout << "Print1\t";
    Print1(arr, 4);
    std::cout << "Print3\t";
    Print2(arr, 4);
    std::cout << "Print2\t";
    Print3(arr, 4);
    Print1(arr, 4);

    // 使用 typedef 声明函数指针
    // typedef void        (*FuncPtr)(int, int);
    // 关键字   函数返回值   函数指针名称  函数形式参数
    // FuncPtr = Swap2;   调用 FuncPtr(a, b);
    // 回调函数(被作为参数传递的函数)
    // void Demo(FuncPtr func, int a, int b) { func(a, b); }

    // 二维数组做函数参数
    // 数组指针 int (*p)[],指针指向 int[]
    // 指针数组 int *p[],是个数组,元素类型为int*
    // 函数指针 int (*fun)(int x,int y);  指向函数的指针
    // 指针函数 int* fun(int x,int y);    返回值为 int* 的函数
    auto Sum = [](int arr[][4], int row) -> int {   // 注意列数不能省略
        std::cout << abi::__cxa_demangle(typeid(arr).name(), 0, 0, 0) << std::endl;
        int ret = 0;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < 4; ++j) {
                std::cout << std::dec <<"arr[" <<i << "][" << j << "] = " << arr[i][j] << "\t";
                ret += arr[i][j];
            }
            std::cout << std::endl;
        }
        return ret;
    };

    int arr2[][4] = {
        {1, 2},
        {3, 4},
        {5, 6},
        {7, 8}
    };        
    int ret = Sum(arr2, 4);
    std::cout << "Sum arr2 value: " << ret << std::endl;
}

void Swap1(int a, int b) {
    a = a + b;
    b = a - b;
    a = a - b;
}
void Swap2(int &a, int &b) {
    a = a + b;
    b = a - b;
    a = a - b;
}
void Swap3(int *a, int *b) {
    *a = *a + *b;
    *b = *a - *b;
    *a = *a - *b;
}

输出结果:
a value : 10
b value : 20
Swap1(int a, int b) :
Swap1 a value : 10
Swap1 b value : 20
Swap2(int &a, int &b) :
Swap2 a value : 20
Swap2 b value : 10
Swap3(int *a, int *b) :
Swap3 a value : 10
Swap3 b value : 20
Swap3 address : 0x1
Print1 int*
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4
Print3 int*
arr[0] = 10 arr[1] = 2 arr[2] = 3 arr[3] = 4
Print2 int*
arr[0] = 20 arr[1] = 2 arr[2] = 3 arr[3] = 4
int*
arr[0] = 30 arr[1] = 2 arr[2] = 3 arr[3] = 4
int (*) [4]
arr[0][0] = 1 arr[0][1] = 2 arr[0][2] = 0 arr[0][3] = 0
arr[1][0] = 3 arr[1][1] = 4 arr[1][2] = 0 arr[1][3] = 0
arr[2][0] = 5 arr[2][1] = 6 arr[2][2] = 0 arr[2][3] = 0
arr[3][0] = 7 arr[3][1] = 8 arr[3][2] = 0 arr[3][3] = 0
Sum arr2 value: 36

  • 函数简单理解就是将某些功能代码模块化,这样可直接调用,而不必每次都写相同的步骤,减少代码冗余
  • c++11 新增了 lambda 表达式,能更直观的体现函数式编程思想
  • c++中,数组、函数等都可当做类型用来定义变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值