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++中,数组、函数等都可当做类型用来定义变量