C++加固

结构体和类

在 C++ 中,struct 和 class 本质上非常相似,唯一的区别在于默认的访问权限:

  • struct 默认的成员和继承是 public 。
  • class 默认的成员和继承是 private 。
  • 可以将 struct 当作一种简化形式的 class ,适合用于没有太多复杂功能的简单数据封装。

例子如下:

struct Books {
	string title; 
	string author; 
	string subject; 
	int book_id; 
	// 构造函数 
	Books(string t, string a, string s, int id) : title(t), author(a), subject(s), book_id(id) {} 
	void printInfo() const {
	 cout << "书籍标题: " << title << endl; 
	 cout << "书籍作者: " << author << endl; 
	 cout << "书籍类目: " << subject << endl; 
	 cout << "书籍 ID: " << book_id << endl; 
	 } 
}; 

void printBookByRef(const Books& book){
	book.printInfo(); 
 }

关键字

extern

extern+变量

extern+变量就是告诉编译器,在其他文件有一个这个变量存在;

extern+函数

当一个函数被声明或定义的时候,extern关键字被隐式地假定了

int foo(int arg1, char arg2); 	// 函数的声明
							 // 编译器会自动认为是 extern int foo(int arg1, char arg2);

static

static + 全局变量

该全局变量只允许在定义的文件中使用,其他文件无法访问。从而解决了在不同文件中定义相同的变量名, 同全局变量也一样,如果不初始化时,默认值是0

static局部变量

同普通局部变量一样,只能在定义的函数中使用,但是与局部变量的不同的是,不管其所在的函数是否被调用过,它会一直存在,而不像其他局部变量那样,会随着函数的被调用或退出而存在或消失。并且static局部变量只赋值一次

static 函数

同static全局变量类似,解决了相同函数名可以在不同文件中定义,同时其他文件无法访问static函数

register变量

register声明告诉编译器,将变量直接放入到寄存器中,因为寄存器的访问速度比内存快,所以register变量可以用于程序中使用频率高的。但编译器可以忽略此选项

注意如下几点:

  1. register变量不允许返回地址
  2. register变量不允许与static公用,考虑一下为什么??
  3. register变量不允许定义成全局变量,只适用于局部变量和函数的形参,否则 在VS会有警告

volatile变量

volatile关键字是防止编译器对变量进行过度优化。保证每次读取该变量时都是从内存中读取,而不是被优化的某个临时寄存器中,一般应用在多线程或者中断服务中

可变参数

va_list	ap;					   // 为了访问可变参数的指针
va_start(va_list ap, argN);		// 初始化va_list, argN表示堆栈中的最后一个变量名称,一般是可变参数的第一个变量
va_arg(va_list ap, type);		// type表示类型
va_end(va_list ap);			   // 结束可变参数遍历

用C风格实现myprintf

#include <iostream>
#include <cstdarg>

void MyPrintf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    
    while (*format) {
        if (*format == '%' && *(format + 1)) { // 处理格式化字符
            ++format;
            switch (*format) {
                case 'd': { // 整数
                    int i = va_arg(args, int);
                    std::cout << i;
                    break;
                }
                case 'c': { // 字符
                    char c = static_cast<char>(va_arg(args, int)); // char 在可变参数中按 int 处理
                    std::cout << c;
                    break;
                }
                case 's': { // 字符串
                    char* s = va_arg(args, char*);
                    std::cout << s;
                    break;
                }
                case 'f': { // 浮点数
                    double f = va_arg(args, double);
                    std::cout << f;
                    break;
                }
                default:
                    std::cout << '%' << *format; // 直接输出不支持的格式
                    break;
            }
        } else {
            std::cout << *format; // 直接输出普通字符
        }
        ++format;
    }
    
    va_end(args);
}

int main() {
    MyPrintf("Hello %s! Your score is %d and height is %f meters.\n", "Alice", 95, 1.75);
    MyPrintf("Character: %c, Integer: %d\n", 'A', 123);
    return 0;
}

为什么在可变参数中 char 要按 int 处理

  • 这是由于 C/C++ 的"默认参数提升"(default argument promotion)规则
  • 当传递可变参数时:
    • char、short 会被提升为 int
    • float 会被提升为 double
  • 所以即使你传入的是 char,在可变参数列表中实际存储的是 int

C++中的类型转换运算符

static_cast<目标类型>(要转换的值)

// 整数转字符
int i = 65;
char c = static_cast<char>(i);  // c 现在是 'A'

// 浮点数转整数
float f = 3.14;
int n = static_cast<int>(f);    // n 现在是 3

// 大类型转小类型
long l = 1000;
short s = static_cast<short>(l); // 把 long 转成 short

预处理

define

带参数

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int x = 5, y = 10;
int z = MAX(x + 1, y * 2);

为什么要加括号?
有括号展开后:

int z = ((x + 1) > (y * 2) ? (x + 1) : (y * 2));

无括号展开后:

int z = x + 1 > y * 2 ? x + 1 : y * 2;

这会导致 x + 1 > y * 2 的优先级低于 ? :,从而产生错误的结果。

字符串化运算符#

#define PRINTVALUE(a)    #a
printf("%s\n", PRINTVALUE(1) );

这里的#a是把PRINTVALUE(a)变成字符串,即printf输出的是字符串"1",不加#号就是数字1;

标记粘贴操作符

## 是标记粘贴操作符,它的作用是将两个标记(a 和 b)连接成一个新的标记;

#define PRINTVALUE(a, b)    a##b

void fun2() {
    printf("This is fun2\n");
}

void test1() {
    printf("This is test1\n");
}

int main() {
    PRINTVALUE(fun, 2)();  // 调用 fun2()
    PRINTVALUE(test, 1)(); // 调用 test1()
    return 0;
}

条件编译

#ifdef#ifndef

#define DEBUG

#ifdef DEBUG
    // 如果 DEBUG 被定义,这段代码会被编译
    printf("Debug mode is on.\n");
#endif

#ifndef RELEASE
    // 如果 RELEASE 没有被定义,这段代码会被编译
    printf("Release mode is off.\n");
#endif

#if#elif#else

#define VERSION 2

#if VERSION == 1
    // 如果 VERSION 等于 1,这段代码会被编译
    printf("Version 1\n");
#elif VERSION == 2
    // 如果 VERSION 等于 2,这段代码会被编译
    printf("Version 2\n");
#else
    // 如果 VERSION 不等于 1 或 2,这段代码会被编译
    printf("Unknown version\n");
#endif

#endif

#endif:用于结束条件编译块。每个 #if#ifdef#ifndef 都必须有一个对应的 #endif

#ifdef#if defined的区别

特性#ifdef#if defined
功能只能检查单个宏是否被定义可以检查单个宏,也可以组合多个条件
灵活性较低较高
逻辑运算符支持不支持支持(如 &&)
可读性简单直观更复杂,但功能更强大
#ifdef DEBUG
    printf("Debug mode is on.\n");
#endif
#if defined(DEBUG) && defined(VERBOSE)
    printf("Verbose debug mode is on.\n");
#elif defined(DEBUG)
    printf("Debug mode is on.\n");
#endif

#error

#error 是 C/C++ 预处理指令之一,用于在编译时强制生成一个错误消息并停止编译过程

#define VERSION 10

#if VERSION < 20
#error "版本太低,不支持编译"
#endif

如果 VERSION 的值小于 20,编译器会停止编译,并输出错误信息:"版本太低,不支持编译"

友元函数

什么是友元函数?

可以访问一个类的私有成员的外部函数;
注意:友元函数虽然在类种,但都不是成员函数!

用在什么情况?

当有两个类,两个类需要互相访问资源的时候;

举例子

可以实现访问不同的类的对象的私有成员:

class B;
class A{
private:
	int valueA;
public:
	A(int a) valueA(a){}
	friend void showSum(A a ,B b);//声明友元函数
};

class B {
private:
    int valueB;
public:
    B(int b) : valueB(b) {}
    friend void showSum(A a, B b); // 同样声明
};


void showSum(A a ,B b){
	std::cout<<"A+B="<<a.valueA + b.valueB<<std::endl;//这里showSum虽然是外部函数,但是直接访问a的private值和b的prvate值,统一调度两个类的成员变量,而不用再在各个类中写get()函数,更方便;
}

当然也可以直接访问同一个类的不同对象的私有成员:

class A{
private:
	int value;
public:
	A(int a) value(a){}
	friend int showSum(A a1 ,A a2);//声明友元函数,虽然函数在类中声明,但这是非成员函数
};

int showSum(A a1,A a2){
	return a1.value + a2.value;//
}

也可以这样写(直接在类中实现函数):

class A{
private:
	int value;
public:
	A(int a) : value(a){}
	friend int showSum(A a1 ,A a2){//该函数不是类的成员函数,但是可以访问对象a1和a2的私有成员变量
		return a1.value + a2.value;
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值