sizeof与strlen
- 含义
- sizeof:是一个操作符,用于计算数据类型或变量的大小(以字节为单位)。在编译时求值
strlen:
是一个函数,用于计算字符串的长度(不包括终止符 '\0')。在运行时求值- 不同点
- 计算时间:strlen是库函数,程序运行时计算长度;sizeof则是在编译时计算长度
- 计算方式:sizeof包括终止符号 \0 。strlen则不包括,只计算字符串的实际长度
- 两者的返回类型都是size_t类型
#include <stdio.h>int main() {int a = 10;double b = 20.0;char c = 'A';char str[] = "Hello, World!";printf("Size of int: %zu bytes\n", sizeof(a)); // 输出变量 a 的大小printf("Size of double: %zu bytes\n", sizeof(b)); // 输出变量 b 的大小printf("Size of char: %zu bytes\n", sizeof(c)); // 输出变量 c 的大小printf("Size of string: %zu bytes\n", sizeof(str)); // 输出数组 str 的大小,包括终止符 '\0'return 0;
}
#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, World!";printf("Length of string: %zu characters\n", strlen(str)); // 输出字符串 str 的长度,不包括终止符 '\0'return 0;
}
static
C和C++中都可以用static去定义静态变量以及静态函数,因为C++是面向对象编程,所以还可以使用static去定义静态成员变量以及静态成员函数等。
static全局和局部静态变量
static 全局静态变量
- 全局静态变量是在文件范围内定义的,并且只能在定义它的文件中访问。它在程序的整个生命周期内都存在,但它的作用域仅限于定义它们的文件
- static全局静态变量,则只可以在定义所在文件中有效,同一个程序中的其他源文件不可以使用。
static局部静态变量
- 局部静态变量是在函数内部定义的,并且只能在定义它们的函数中访问
- 存储在静态存储区中,生命周期是贯穿在整个程序的运行期间
static全局静态变量和static局部静态变量区别总结
作用域:
- 全局静态变量:仅限于定义它的文件中
- 局部静态变量:仅限于定义它们的函数
生命周期:
- 全局静态变量:在程序的整个生命周期内都存在。
- 局部静态变量:在程序的整个生命周期内都存在,但它的值在函数调用之间保留
存储位置:
- 全局静态变量:存储在静态数据区
- 局部静态变量:存储在静态数据区,而不是栈区
- 两者都存储静态数据区中
// 全局静态变量
static int global_static_var = 0;void increment_global_static_var() {global_static_var++;printf("全局静态变量: %d\n", global_static_var);
}int main() {increment_global_static_var(); // 输出:1increment_global_static_var(); // 输出:2return 0;
}
#include <stdio.h>void increment_local_static_var() {// 局部静态变量static int local_static_var = 0;local_static_var++;printf("局部静态变量: %d\n", local_static_var);
}int main() {increment_local_static_var(); // 输出:1increment_local_static_var(); // 输出:2increment_local_static_var(); // 输出:3return 0;
}
static静态函数
static静态函数
- 作用域:只可以在定义该函数的文件内部调用,不可以从其他文件中访问该函数
- 作用:定义的函数只放在该文件中使用,避免命名造成的冲突
代码说明:两段代码是放在不同的文件
static静态成员变量
static静态成员变量
- 声明:类内声明,类外定义初始化。可以通过类名直接访问,不需要创建对象
- 作用域:静态成员变量在整个进程生命周期内都存在
- 特点
- 共享性:所有类对象共享一个静态成员变量
- 静态成员变量可以作为成员函数的参数,普通成员变量不可以
- 静态成员变量的类型可以用该类自己的类型,而普通成员变量只可以用该类的指针或者引用
- 唯一性:无论创建多少对象,静态成员变量在内存中只占用同一块内存空间
#include <iostream>class MyClass {
public:// 静态成员变量声明static int staticVar;// 成员函数,用于显示静态成员变量的值void display() const {std::cout << "Static Variable: " << staticVar << std::endl;}
};// 静态成员变量定义(注意记忆)
int MyClass::staticVar = 0;int main() {// 通过类名访问静态成员变量MyClass::staticVar = 5;MyClass obj1;MyClass obj2;obj1.display(); // 输出:Static Variable: 5obj2.display(); // 输出:Static Variable: 5// 修改静态成员变量的值MyClass::staticVar = 10;obj1.display(); // 输出:Static Variable: 10obj2.display(); // 输出:Static Variable: 10return 0;
}
static静态成员函数
static静态成员函数
- 作用域:静态成员函数是没有this指针的,所以不可以调用非静态成员或者非静态成员函数,可以类内声明类外定义
- 特点
- 静态成员函数是类作用域的全局函数
- 静态成员函数不可以声明成virtual const volatile函数
- 独立:静态成员函数是独立于任何对象的,因为不可以访问类的非静态成员变量或非静态成员函数
- 优点
- 静态成员中可以封装与对象无关的逻辑,从而实现类内共享
- 静态成员函数不需要创建对象就可以调用,所以可以节省内存资源
#include <iostream>class MyClass {
public:// 静态成员函数声明static void staticFunction();// 非静态成员函数声明void nonStaticFunction() {std::cout << "This is a non-static member function.\n";}private:int nonStaticVar; // 非静态成员变量
};
static对象
static对象
- 静态全局对象:整个程序运行期间存在,但是其作用域只限于定义它的文件中
- 静态局部对象:函数内定义,只可以在定义它的作用域中访问,第一次调用的时候初始化,在程序的整个生命周期都保留该数值,不会再次初始化。
- 类的静态成员对象:每个类只拥有一个静态成员对象的实例,并且所有对象共享这个对象
#include <iostream>static int globalStaticVar = 0; // 静态全局对象void incrementGlobalStaticVar() {globalStaticVar++;std::cout << "Global static variable: " << globalStaticVar << std::endl;
}int main() {incrementGlobalStaticVar(); // 输出:Global static variable: 1incrementGlobalStaticVar(); // 输出:Global static variable: 2return 0;
}
#include <iostream>void incrementLocalStaticVar() {static int localStaticVar = 0; // 静态局部对象localStaticVar++;std::cout << "Local static variable: " << localStaticVar << std::endl;
}int main() {incrementLocalStaticVar(); // 输出:Local static variable: 1incrementLocalStaticVar(); // 输出:Local static variable: 2incrementLocalStaticVar(); // 输出:Local static variable: 3return 0;
}
#include <iostream>class MyClass {
public:static MyClass staticInstance; // 静态成员对象声明void display() const {std::cout << "This is a static member object." << std::endl;}
};// 静态成员对象定义
MyClass MyClass::staticInstance;int main() {MyClass::staticInstance.display(); // 通过类名访问静态成员对象MyClass obj;obj.staticInstance.display(); // 通过对象访问静态成员对象(不推荐)return 0;
}
const
总结:
- const常量:const修饰变量或者成员变量,可以进行类型检查,从而节省内存空间提高效率
- const函数参数:传递过来函数参数的数值不可以改变
- const成员函数:该成员函数不可以修改任何类型的成员变量(mutable修饰的变量可以),同时该函数不可以调用非const的成员函数
const 变量
const 变量
- 全局const变量:整个程序色生命周期存在,不可以修改,其他文件通过extern声明也可以访问
- 局部const变量:函数或代码块中定义,只可以在定义的作用域中访问
- 类的const成员变量:类中声明的const成员变量,必须在构造函数初始化列表中进行初始化
#include <iostream>const int globalConstVar = 100; // 全局 const 变量int main() {std::cout << "全局 const 变量: " << globalConstVar << std::endl;// globalConstVar = 200; // 错误:不能修改 const 变量return 0;
}
#include <iostream>void displayLocalConst() {const int localConstVar = 50; // 局部 const 变量std::cout << "局部 const 变量: " << localConstVar << std::endl;// localConstVar = 100; // 错误:不能修改 const 变量
}int main() {displayLocalConst();return 0;
}
#include <iostream>class MyClass {
public:const int memberConstVar; // 类的 const 成员变量MyClass(int val) : memberConstVar(val) {} // 在构造函数初始化列表中初始化void display() const {std::cout << "类的 const 成员变量 " << memberConstVar << std::endl;}
};int main() {MyClass obj(10);obj.display();// obj.memberConstVar = 20; // 错误:不能修改 const 成员变量return 0;
}
const指针和指向const的指针
const指针和指向const的指针
- 指向const的指针:指针指向的值不可以通过指针对该值进行修改,但是指针本身可以改变
- const指针:指针本身是常量,不能够改变指向,但是指向的数值可以修改
- 指向const的const指针:指针本身和指针指向都不可以修改
- 注意:两种写法不同,注意记忆
#include <iostream>int main() {int value = 30;const int* ptr = &value; // 指向 const 的指针std::cout << "Value: " << *ptr << std::endl;// *ptr = 40; // 错误:不能通过 ptr 修改值int anotherValue = 50;ptr = &anotherValue; // 可以改变指针本身std::cout << "Another value: " << *ptr << std::endl;return 0;
}
#include <iostream>int main() {int value = 30;int* const ptr = &value; // const 指针std::cout << "Value: " << *ptr << std::endl;*ptr = 40; // 可以修改指向的值std::cout << "Modified value: " << *ptr << std::endl;// int anotherValue = 50;// ptr = &anotherValue; // 错误:不能改变指针本身return 0;
}
#include <iostream>int main() {int value = 50;const int* const ptr = &value; // 指向 const 的 const 指针std::cout << "Value: " << *ptr << std::endl;// *ptr = 60; // 错误:不能修改指向的值// int anotherValue = 70;// ptr = &anotherValue; // 错误:不能改变指针本身return 0;
}
const引用
const引用:const引用指向const对象的应用,可以读取变量,但是不可以通过应用修改指向的数值
#include <iostream>void display(const int& ref) {std::cout << "Reference: " << ref << std::endl;// ref = 20; // 错误:不能通过 const 引用修改值
}int main() {int value = 10;display(value);return 0;
}
define 和 const
define 和 const
- 含义
#define
是一个预处理指令,用于定义宏。编译之前,预处理器会用宏的值替换程序中的所有宏名。没有类型检查,也不会在调试信息中出现const
是一个关键字,用于定义类型安全的常量变量。const
变量在程序运行期间受类型检查,并且可以用于更复杂的数据类型,如数组、指针和对象- 特点:
- 编译阶段:define是在编译预处理阶段进行替换,没有时间开销。const则是在编译阶段确定数值,会有时间开销。
- 安全性:define只是进行简单的代码替换,不会进行类型安全检查;const定义的常量是由类型,所以会进行类型安全检查
- 存储空间:define浪费空间,程序每次替换的时候都会在内存中备份,替换越多备份越多 ;const节省空间,因为存储在静态区。
- 调试:define定义的宏不可以调试,预编译阶段已经替换了。const定义的常量可以进行调试
- define可以接受参数构造复杂的表达式,const不可以接受参数构造复杂的表达式。
inline
- 含义
- inline关键字用于提示编译器将函数定义为内联函数,内联函数的目的在于减少函数调用的开销从而提高程序的运行效率
- 内联函数代码会在每次调用的时候直接插入到调用点,避免函数调用不必要的开销,内联只是一个建议,编译器可以选择忽略它
- 特点
- 减少函数调用开销:inline是一个关键字,像普通的函数一样被调用,而不是直接在调入点直接展开,从而减少调用函数带来的开销,提高程序运行效率
- 使用
- 类内定义成员函数默认就是内联函数,虚函数除外(虚函数运行时决定,编译时无法确定的虚函数的实际调用)
- 类外定义成员函数,需要加上inline关键字
#include <iostream>class MyClass {
public:inline void display() {std::cout << "内联函数运行" << std::endl;}
};int main() {MyClass obj;obj.display();return 0;
}
new malloc delete free
malloc
malloc
- 含义:该库函数用于动态分配内存,运行的时候从堆内存中分配指定大小的字节,并返回指向该内存块的指针。malloc分配的内存未初始化,所以内部的数据不确定
原理分析
- 进程启动后,操作系统会给该进程分配一个初始的堆区域,同时初始化一个数据结构(此处按照链表梳理)
- 第一次调用malloc的时候,内存管理器再空闲链表中查找一个足够大的空间,然后分配,如果失败则返回null
- 是进程调用free的时候,内存管理器释放该链表所占用的内存空间
#include <stdio.h>
#include <stdlib.h>int main() {int* ptr;ptr = (int*)malloc(10 * sizeof(int)); // 分配10个整数大小的内存if (ptr == NULL) {printf("Memory allocation failed\n");return 1;}for (int i = 0; i < 10; i++) {ptr[i] = i * 10;}for (int i = 0; i < 10; i++) {printf("%d ", ptr[i]);}free(ptr); // 释放内存return 0;
}
new 和 malloc的区别
new 和 malloc的区别
- 初始化:new申请内存时,会调用对象的构造函数,然后对对象初始化;malloc会在堆中申请一块指定大小的内存空间,仅分配内存,不会初始化
- new指定内存空间初始化对象,malloc只可以在堆中申请内存
- new是C++的操作符,malloc是C中的一个函数
- 返回值:new返回值是一个对象的指针类型,malloc则返回void*指针
- 空间大小:new的空间大小是由编译器自动计算的,malloc是需要指定其空间大小;new的空间大小不可以改变
#include <iostream>class MyClass {
public:MyClass() {std::cout << "Constructor called" << std::endl;}~MyClass() {std::cout << "Destructor called" << std::endl;}
};int main() {// 使用 new 分配单个对象MyClass* obj = new MyClass();// 使用 delete 释放内存delete obj;// 使用 new 分配数组MyClass* arr = new MyClass[3];// 使用 delete[] 释放数组内存delete[] arr;return 0;
}
#include <iostream>
#include <cstdlib>class MyClass {
public:MyClass() {std::cout << "Constructor called" << std::endl;}~MyClass() {std::cout << "Destructor called" << std::endl;}
};int main() {// 使用 malloc 分配内存MyClass* obj = (MyClass*)malloc(sizeof(MyClass));// 需要手动调用构造函数new (obj) MyClass(); // 使用定位 new 调用构造函数// 使用 free 释放内存前需要手动调用析构函数obj->~MyClass();free(obj);// 使用 malloc 分配数组MyClass* arr = (MyClass*)malloc(3 * sizeof(MyClass));// 需要手动调用构造函数for (int i = 0; i < 3; ++i) {new (&arr[i]) MyClass(); // 使用定位 new 调用构造函数}// 使用 free 释放数组内存前需要手动调用析构函数for (int i = 0; i < 3; ++i) {arr[i].~MyClass();}free(arr);return 0;
}
delete和free的区别
delete和free的区别
- delete是C++的一个操作符,可以进行重载;free是C中的一个函数,不可以进行重载
- free只会释放指向的内存,不会执行对象的析构函数;delete则可以执行对象的析构函数
- delete和delete[ ]
- 释放 new 和 new[ ]分配的内存
- 适当new分配的单个对象,释放new[ ] 分配的数组
- free
- 用法:释放malloc 、calloc 、realloc分配的内存
- 仅释放内存,不调用析构函数
volatile
含义
- 该关键字用于修饰变量,告诉编译器该变量在程序运行期间可能被改变,因为贬义词不能够对这个变量进行优化,需要每次访问该变量的时候都重新获取其数值
volatile的作用
- 阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回
- 阻止编译器调整操作volatile变量的指令排序
volatile使用场景分析
- 多线程编程:volatile变量可以被多个线程修改,使用该关键字从而确保每个线程从内存中读取最新的数值,而不是从寄存器或者缓存中读取的旧值
限制
- 非线程安全:该关键字可以确保每次读取变量的时候都是从内存中获取的最新值,但是不保证其原子性,所以在多线程中,需要使用互斥锁或者原子操作来保证其线程安全
- 不适合硬件优化:如果CPU缓存或者其他硬件优化,则需要使用其他方式,内存栅栏(memory barriers)或 C++11 中的
std::atomic
。
//多线程场景下的使用#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>volatile bool stopFlag = false;void worker() {while (!stopFlag) {std::this_thread::sleep_for(std::chrono::milliseconds(100));}std::cout << "Worker thread exiting." << std::endl;
}int main() {std::thread t(worker);std::this_thread::sleep_for(std::chrono::seconds(1));stopFlag = true;t.join();return 0;
}
宏
使用宏实现比较大小以及两个数的最小值
#include <iostream>
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
using namespace std;int main ()
{int var1 = 10, var2 = 100;cout << MAX(var1, var2) << endl; //100cout << MIN(var1, var2) << endl; //10return 0;
}
宏定义和内联函数的区别
宏定义和内联函数的区别
- 内联函数在编译的时候展开,宏则在编译预处理的时候展开;
- 内联函数展开后是嵌入到代码中,宏只是进行简单的文本替换
- 内联函数是真正的函数会对参数类型以及语句进行检查
- 内联函数在调用点直接展开,避免函数参数压栈,减少开销
- 宏只是简单的对文本进行替换,所以容易出错
- 内联函数可以调试,宏函数无法调试
使用场景分析
- 宏:定义简单的常量和轻量级的宏功能,负责的宏避免使用
- 内联函数:内联函数比宏更安全且容易调试和维护
C和C++中的struct区别
含义
C语言中:struct
主要用于将多个不同类型的数据组合在一起形成一个复合数据类型。结构体中的成员默认是公开的(public),但它们不支持方法、构造函数、析构函数和访问控制- 在C++ 中:
struct
保留了 C 中的所有特性,但它增加了许多新的功能,使其更接近于类(class
)。在 C++ 中,struct
可以包含成员函数、构造函数、析构函数、访问控制和继承等特性。默认情况下,struct
的成员是公开的(public),而class
的成员是私有的(private)两者区别总结
- 数据类型不同:C语言中是用户自定义的数据类型,C++中则是抽象数据类型,支持成员函数定义
- 访问权限不同:C语言中的没有访问权限设置,不能够定义成员函数;C++中的struct和类一样,由访问权限可以定义成员函数
- 定义变量:C语言中定义struct类型的变量时,需要加上struct关键字,但是C++ 中的struct可以不加关键字
- 继承与多态:C++中的可以继承也可以对应实现多态
struct与union区别
- 内存空间:union联合体中的所有变量共享同一段内存空间,struct中的每个成员变量独占内存空间
- 成员:联合体只有一个有效成员(内部是有多个不同数据成员组成),结构体中的所有成员都有效
- 成员赋值:联合体不同成员赋值的时候,会覆盖其他成员的值,但是对于结构体给不同成员赋值,则不会影响
- 内存大小:联合体的大小是所有变量的最大值,按照最大值类型的倍数进行分配大小;结构体则是按照内存对齐原则
- struct可以定义变长数组成员变量 int a [] ,union中不可以包含这种不确定长度的变量
//struct
#include <stdio.h>struct Point {int x;int y;
};int main() {struct Point p;p.x = 10;p.y = 20;printf("Point: (%d, %d)\n", p.x, p.y);return 0;
}
//union事例
#include <stdio.h>union Data {int i;float f;char str[20];
};int main() {union Data data;data.i = 10;printf("data.i: %d\n", data.i);data.f = 220.5;printf("data.f: %f\n", data.f);// 注意:此时访问 data.i 会得到未定义的值printf("data.i: %d\n", data.i); // 由于 union 共用内存,data.i 的值已被 data.f 覆盖return 0;
}
//混合使用
#include <stdio.h>struct Mixed {int type;union {int intValue;float floatValue;char strValue[20];} data;
};int main() {struct Mixed m;m.type = 0; // 表示 int 类型m.data.intValue = 10;printf("Type: %d, IntValue: %d\n", m.type, m.data.intValue);m.type = 1; // 表示 float 类型m.data.floatValue = 220.5;printf("Type: %d, FloatValue: %f\n", m.type, m.data.floatValue);m.type = 2; // 表示字符串类型snprintf(m.data.strValue, 20, "Hello, World!");printf("Type: %d, StrValue: %s\n", m.type, m.data.strValue);return 0;
}
extern C的作用
含义
- extern C是一个链接规范,指示编译器按照C语言的方式处理被包含的代码
- 目的是解决C++和C兼容性的问题,尤其在链接阶段
- 让C++编译器生成与C兼容的符号名,从而让C++代码可以调试C代码
事例(一个用C写的函数,一个CPP代码,采用C的方式连接)
// c_code.c
#include <stdio.h>void c_function() {printf("This is a C function.\n");
}
// cpp_code.cpp
#include <iostream>// 告诉编译器按 C 语言的方式链接 c_function
extern "C" void c_function();int main() {c_function();return 0;
}
strcpy函数缺陷
- 作用
- C++中的一个标准函数,其会将函数' \0' 结束符的字符串复制到另一个地址空间,返回值的类型为char*,返回值为拷贝后的字符串首地址
- 缺陷
- 缓冲区溢出:不检查目的缓冲区的大小边界,而是将源字符串逐一全部复制到到新内存空间中,同时加上字符串的终止符号,但是这种行为会导致其他变量被覆盖
- 不安全复制:如果源字符串没有以空字符 \0 结尾,那么strcpy就会一直复制,直到内存中的随机空字符
- 避免缺陷
- 使用strncpy 代替
- strcpy的安全版本,允许指定最大复制的字符数,从而防止缓冲区的溢出
- 如果源字符串长度大于指定的最大字符数,目标字符串将不会自动添加空字符
- 动态内存分配
#include <stdio.h>
#include <string.h>int main() {char src[] = "This is a long string that will not cause buffer overflow";char dest[10];strncpy(dest, src, sizeof(dest) - 1);dest[sizeof(dest) - 1] = '\0'; // 确保目标字符串以空字符结尾printf("Destination: %s\n", dest);return 0;
}
lambda 表达式
- capture:用于捕获外部变量,可以按值捕获(
=
)或按引用捕获(&
)。- parameters:函数参数列表,与普通函数相同。
- return_type:返回类型(可选,如果编译器能推断出返回类型,则可以省略)。
- function body:函数的具体实现
基本lambda表示使用
#include <iostream>int main() {auto add = [](int a, int b) -> int {return a + b;};int result = add(3, 4);std::cout << "Result: " << result << std::endl;return 0;
}
捕捉外部变量
#include <iostream>int main() {int x = 10;int y = 20;// 按值捕获(复制变量)auto addValueCapture = [x, y]() {return x + y;};// 按引用捕获(引用变量)auto addReferenceCapture = [&x, &y]() {return x + y;};std::cout << "Value Capture: " << addValueCapture() << std::endl;std::cout << "Reference Capture: " << addReferenceCapture() << std::endl;return 0;
}
捕捉所有外部变量
#include <iostream>int main() {int x = 10;int y = 20;// 按值捕获所有外部变量auto addAllValueCapture = [=]() {return x + y;};// 按引用捕获所有外部变量auto addAllReferenceCapture = [&]() {return x + y;};std::cout << "All Value Capture: " << addAllValueCapture() << std::endl;std::cout << "All Reference Capture: " << addAllReferenceCapture() << std::endl;return 0;
}
实现排序
#include <algorithm>
#include <iostream>
#include <vector>int main() {std::vector<int> numbers = {5, 2, 9, 1, 5, 6};// 使用 lambda 表达式进行升序排序std::sort(numbers.begin(), numbers.end(), [](int a, int b) {return a < b;});std::cout << "Sorted numbers: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;return 0;
}
C++14
C++14,lambda函数形参允许泛型和初始化捕捉
C++14中,lanbda表达式支持泛型参数,从而使得可以编写类型无关的lambda表达式
#include <iostream>
#include <vector>
#include <algorithm>int main() {auto add = [](auto a, auto b) {return a + b;};std::cout << "Sum of integers: " << add(1, 2) << std::endl; // 输出 3std::cout << "Sum of doubles: " << add(1.5, 2.3) << std::endl; // 输出 3.8std::cout << "Concatenation of strings: " << add(std::string("Hello, "), std::string("World!")) << std::endl; // 输出 Hello, World!return 0;
}
初始化捕捉:允许在捕获列表中直接初始化捕获的变量。主要作用在于捕捉并初始化一个新的变量时无需定义一个临时变量
#include <iostream>int main() {int x = 10;int y = 20;auto lambda = [z = x + y]() {std::cout << "Captured sum: " << z << std::endl;};lambda(); // 输出 Captured sum: 30return 0;
}
explicit
explicit关键字用于C++的构造函数中,用于防止隐式转换
#include <iostream>
using namespace std;class MyClass {
public:explicit MyClass(int value) : m_value(value) {}void printValue() const {cout << "Value: " << m_value << endl;}private:int m_value;
};void display(MyClass obj) {obj.printValue();
}int main() {MyClass obj1(10); // 正确,显式调用构造函数obj1.printValue();// MyClass obj2 = 20; // 错误,隐式转换被explicit阻止MyClass obj2 = MyClass(20); // 正确,显式调用构造函数obj2.printValue();display(obj1); // 正确,显式调用// display(30); // 错误,隐式转换被explicit阻止display(MyClass(30)); // 正确,显式调用构造函数return 0;
}
define和typedef
define是一个预处理指令,用于定义符号常量和宏,编译前,预处理器用定义的值替换调代码中的符号
#include <iostream>
#define PI 3.14159
#define SQUARE(x) ((x) * (x))int main() {std::cout << "Value of PI: " << PI << std::endl;std::cout << "Square of 5: " << SQUARE(5) << std::endl;return 0;
}
typedef:为现有的类型创建一个新名称
#include <iostream>typedef unsigned long ulong;
typedef int (*func_ptr)(int, int);int add(int a, int b) {return a + b;
}int main() {ulong myNumber = 1000;std::cout << "Value of myNumber: " << myNumber << std::endl;func_ptr fptr = add;std::cout << "Sum of 3 and 4: " << fptr(3, 4) << std::endl;return 0;
}
两者比较
- define:用于定义符号常量和宏,预处理阶段替换,代码可读性差
- typedef:为类型创建别名,在编译阶段进行替换,代码可读性较好
class和struct
两者区别
- 默认访问权限:class成员默认是私有的,struct成员默认是公有的
- 使用场景:class通常用于定义复杂的数据结构和具有成员函数对象,struct则反之
//注意,struct也可以定义公有、私有、保护成员
struct ExampleStruct {int publicValue; // 公有成员private:int privateValue; // 私有成员protected:int protectedValue; // 保护成员
};
sizeof(1==1)在C和C++中结果分析
1==1在C和C++结果不同的原因
- 本质上是一个比较操作符,判断两个整数是否相等,所以在C和C++中返回的是一个布尔值
- C中的布尔值是用int类型表示,所以1==1则返回1
- C++在C++98标准开始,有自己的专属bool类型,所以会返回true
sizeof(1==1)
- C中sizeof(1==1) 等价于 sizeof(int),因为int类型大小是4字节,所以最终结果是4
- C++中布尔类型占有1个字节,所以返回结果为1
memmove函数
memmove
- C标准库的函数,用于在内存中移动数据,比memcpy更安全,因为它处理了重叠区域情况
- 作用:将原地址所指向的某个内存区域中的数据复制到目标地址所指向的另一个内存区域中
- 参数解释
- dest:目标内存指针
- src:源内存指针
- n:复制字节数
- 返回值:返回dest
原理简析
- 内部检查源地址和目标地址是否重叠,如果重叠则会从src的末尾开始复制,以避免覆盖源数据
#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, World!";printf("Original string: %s\n", str);// 使用 memmove 进行重叠内存区域的移动memmove(str + 7, str, 6); // 将 "Hello," 移动到 " World!" 之前printf("After memmove: %s\n", str);return 0;
}
auto
auto(自动类型推导,编译器根据初始化表达式的类型来推导变量类型)
#include <vector>
#include <iostream>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 使用 auto 来简化迭代器声明for (auto it = vec.begin(); it != vec.end(); ++it) {std::cout << *it << " ";}return 0;
}
推导规则总结
- 等式后是引用,则取出引用;如果auto后有&,则不去除
- 顶层的const会被去除
new
new用于动态分配内存,为对象或者的数据类型分配相应的内存,并返回指向该内存的指针。new不仅分配内存,还调用构造函数来初始化对象
- new和delete原理分析
- new分配内存的时候,会调用操作系统的内存分配函数(一般情况下是malloc),然后返回指向分配内存的指针
- new然后使用构造函数来初始化对象
- delete释放内存的时候,调用对象的析构函数,然后调用操作系统内存释放函数
- 注意
- 内存泄漏:每次使用完new分配的内存后必须使用delete释放,否则会导致内存泄漏
- 智能指针:尽量使用智能指针防止内存泄漏
- new分配数组的时候,必须使用delete[ ] 释放内存
#include <iostream>class MyClass {
public:MyClass() {std::cout << "Constructor called!" << std::endl;}~MyClass() {std::cout << "Destructor called!" << std::endl;}
};int main() {MyClass* objArray = new MyClass[3]; // 分配 MyClass 类型的对象数组,调用构造函数delete[] objArray; // 释放内存,调用析构函数return 0;
}