前端?C++ ?较大差异基础罗列
发布时间:2026/10/9 10:12:51 锦皓数字建站

文章目录Basic1、std::是什么2、 取地址符和 * 的含义3、C中的遍历4、C中数组和动态数组vector5、map set 常用操作6、static7、nullptr现代空指针8、ecplicit禁止隐式转换9、构造函数的初始化调用析构函数1构造函数2析构函数10、const11、virtual虚拟函数和override重写12、- 和 . 的区别13、constexpr编译期计算14、你还不知道using的几种用法吗15、enum class强类型枚举16、C 类型转换Basic1、std::是什么auto dstd::string(world);std::coutd ;std是standard标准的缩写它是 C 标准库的命名空间namespace。如果不想每次都写std::可以在文件开头写using namespace std;之后就可以直接写cout、string、vector了。但在大型项目中不推荐这样做因为不同库可能有同名函数会产生命名冲突。2、取地址符和*的含义int x10;const int* px;// p 指向 xp 记住了 x 的地址 // *p20;// ❌ 不行const 说指针指向的内容不能改x30;// ✅ 可以直接改 x 本身没问题 std::cout*p\n;// 输出30因为 p 指向 xx 已经变成了30//x → 0x01 x 的地址 // p → 0x01 p 里存的值就是 x 的地址 // *p →10顺着地址找到里面存的值 //其他示例 int i42;intri;// ① 声明时的引用别名 r等同于i是i的别名 int* pi;// ② 表达式中的取地址p是声明的一个指针、指向i的地址用i获取 最终各变量的值 变量 值 含义 i42整数 r42i 的别名和 i 完全一样 p 0x7ffd5a3c某个地址 i 的内存地址 *p42通过地址找到的值就是 ii 0x7ffd5a3c某个地址 i 的内存地址符号什么时候含义口语理解x表达式中取 x 的地址“x 住哪儿”int* p声明时p 是指针“p 是个专门存地址的变量”*p表达式中取 p 指向的值“顺着地址看看里面是谁”其他注意在遍历的时候如果想对原数值进行修改一定要遍历读取引用否则是复制一份的数据修改不了原来数组std::vectorintscores{12,23,45,123,345};for(intscore:scores){score;}//score原数组中的值 //score原数组中的值的地址3、C中的遍历使用:#include vectorauto vecstd::vector{1,2,3};// 写法一传统 for和 JS/C/Java 一样for(int i0;ivec.size();i){std::coutvec[i] ;}// 写法二范围 for更简洁推荐for(auto x:vec){std::coutx ;}4、C中数组和动态数组vectorint vecstd::vector{1,2,3};//动态数组自动扩容不固定大小 int arr[]{1,2,3};//常规数组数组常用操作auto vecstd::vector{1,2,3};vec.push_back(4);//末尾加 vec.size();//长度计算 vec[0];//取值 vec.pop_back();//删除末尾 vec.empty();//判断是否为空5、map set 常用操作map集合std::mapstring,intages{{jack,18},{eric,12}};//声明 ages[jack];//18 ages[vicky]18;ages.size();//3 ages.count(bob);//false,count是判断是否存在 for(auto[name,age]:ages){std::coutnameisage\n;}set不重复几何std::setintnums{1,2,3,4,5};nums.insert(9);//插入 nums.erase(2);//擦除 nums.count(5);//是否存在 numns.size();//大小6、staticstatic声明的变量只初始化一次并且在函数调用执行完成之后不会销毁voidcounter(){static int count0;count;std::coutcount\n;}intmain(){counter();//1 counter();//2 counter();//3}7、nullptr现代空指针比 NULL 更安全、更清晰避免类型混淆。NULL其实是 C 语言的宏本质就是整数0。nullptr有自己专属的类型std::nullptr_t只能赋值给指针不会被当成整数。nullptr 的类型就是指针。int* pnullptr;// 空指针安全if(pnullptr){// 判断指针是否为空 std::coutp 是空指针\n;}// 也可以简写为if(!p){// 等价写法 std::coutp 是空指针\n;}8、ecplicit禁止隐式转换ecplicit Distance(double m) : meters(m) {}#include iostreamclass Distance{private: double meters;public: // explicit Distance(double m):meters(m){}// 加explicit更安全 Distance(double m):meters(m){}// 不加会隐式转换 double get()const{returnmeters;}};void print(double d){std::cout距离: d 米\n;}intmain(){Distance d500;// 如果构造函数不是explicit这里允许 print(d);// 隐式把 Distance → double // print(500);// 如果加了explicit这行会报错}9、构造函数的初始化调用析构函数1构造函数这真的前端看到震惊写法孤陋寡闻了#include iostreamclass Distance{private: double meters;public: Distance(double m):meters(m){}// 不加会隐式转换 double get()const{returnmeters;}};intmain(){Distance d500;// 如果构造函数不是explicit这里允许 print(d);// 隐式把 Distance → double // print(500);// 如果加了explicit这行会报错}构造函数定义和类名同名的函数就是构造函数写法、功能分析Distance(double m) : meters(m) {}Distance(double m):meters(m){}//构造函数,功能同下函数体内赋值 Distance(double m){metersm;}//WHY为什么多此一举这样写因为有些情况下函数体内赋值做不到必须用初始化列表 //如下 class Example{const intid;// const 成员必须在创建时就赋值 const std::stringname;// 引用成员必须在创建时就绑定 // ✅ 初始化列表——创建时直接赋值 Example(int i, std::stringn):id(i), name(n){}// ❌ 函数体内赋值——做不到 Example(int i, std::stringn){idi;// 报错const 不能赋值 namen;// 报错引用不能重新绑定}};Distance d 500 为什么可以Distance d500;//自动翻译成下面 Distance dDistance(500);//同Distance d{500};多个变量值怎么初始化构造函数class Box{double length, width, height;public: Box(double l, double w, double h):length(l), width(w), height(h){}};//初始化 // 写法一直接初始化最常用 Box b(1.0,2.0,3.0);// 写法二列表初始化C11推荐 Box b{1.0,2.0,3.0};// 写法三先构造再赋值 Box bBox(1.0,2.0,3.0);// 写法四列表初始化 autoC17最简洁 auto bBox{1.0,2.0,3.0};2析构函数符号名字作用什么时候调用没有~构造函数对象出生时自动调用创建对象时有~析构函数对象死亡时自动调用销毁对象时class Base{public:Base(){// 构造函数出生时调用 std::cout我出生了\n;}~Base(){// 析构函数死亡时调用 std::cout我死了\n;}};intmain(){Base b;// 输出我出生了}// 离开作用域输出我死了为什么析构函数要加virtual防止内存泄漏。看这个场景classBase{public:~Base(){std::coutBase 析构\n;}// 没有 virtual};classDog:publicBase{public:~Dog(){std::coutDog 析构\n;}};Base*petnewDog();deletepet;// 输出Base 析构 ← 只调了 Base 的Dog 的部分没清理加了virtual之后class Base{public: virtual ~Base(){std::coutBase 析构\n;}// 有 virtual};Base* petnew Dog();delete pet;// 输出Dog 析构 ← 先调 Dog 的// Base 析构 ← 再调 Base 的// 完整清理没有泄漏10、constconst跟在函数后面意思是“这个函数承诺不修改对象内部的任何数据”class Dog:public Animal{std::string name旺财;void speak()const{// ✅ 可以读数据 std::coutname 汪汪汪\n;// ❌ 不能改数据编译器会报错 // name小白;}void rename(std::string n){// 没有 const namen;// ✅ 可以改因为这个函数没承诺只读}};const 对象只能调用 const 函数const Dog myDog;// const 对象 myDog.speak();// ✅ 可以speak()有 const承诺不改 myDog.rename(小白);// ❌ 报错rename()没 constconst 对象不能调用11、virtual虚拟函数和override重写有了virtual编译器会去看实际创建的对象是什么然后调用对应的函数。class Animal{public: virtual voidspeak(){std::cout某种动物叫声\n;}};class Dog:public Animal{public: void speak()override{std::cout汪汪汪\n;}};Animal* petnew Dog();pet-speak();// 输出汪汪汪 // ✅ 程序运行时发现实际对象是 Dog调用 Dog 的 speak其他问题答案为什么叫虚函数因为调用哪个版本的函数要等到运行时才确定“不确定所以叫虚”只有虚函数能重写吗是的只有父类的virtual函数才能被子类overridevirtual的作用让编译器在运行时根据实际对象类型调用正确的函数多态override的作用告诉编译器我要重写父类的虚函数写错了编译器会报错12、-和.的区别pet-speak();等同于pet.speak();吗?取决于pet是指针还是对象pet 的类型调用方式示例指针Animal* pet用-pet-speak();对象Animal pet用.pet.speak();并且-是简写pet-speak();// 等价于(*pet).speak();// 意思是先通过地址找到对象*pet再调用方法.speak()13、constexpr编译期计算constexpr “这个值/计算在编译时就能搞定”。好处是更快、更安全。但不能用于有 I/O、动态内存等运行时才能完成的操作。// ✅ 可以加 constexpr constexpr int add(int a, int b){returna b;// 简单的计算没问题}// ❌ 不能加 constexpr constexpr voidprint_hello(){std::couthello\n;// ❌ 有 I/O 操作编译时做不了}// ❌ 不能加 constexpr constexpr intget_random(){returnrand();// ❌ 每次结果不同编译时确定不了}限制能做的不能做的简单的数学计算std::cout等 I/O 操作条件判断if调用非constexpr的函数递归C14 起使用new动态内存返回常量修改外部状态constexpr变量 vsconst变量constconstexpr含义运行时不能改编译时就能确定何时确定值可以是运行时必须编译时const int xget_input();// ✅ 运行时才确定但之后不能改 constexpr int yget_input();// ❌ 编译时确定不了报错 constexpr int z100;// ✅ 编译时就知道是10014、你还不知道using的几种用法吗用法一引入命名空间usingnamespacestd;// 引入整个 std 命名空间// 之后可以直接写 cout不用写 std::cout用法二引入命名空间中的某一个成员usingstd::cout;// 只引入 cout不引入其他的using std::endl; // 只引入 endlcouthelloendl;// ✅ 可以直接用// cin x; // ❌ 不行没引入 cin比using namespace std;更安全因为只引入了你需要的东西不会产生命名冲突。用法三给类型起别名类型别名usingStringVecstd::vectorstd::string;// 新名字↑ 原来的类型意思是以后StringVec就等价于std::vectorstd::string。StringVec v2{苹果,香蕉};// 等价于std::vectorstd::stringv2{苹果,香蕉};15、enum class强类型枚举#include iostreamenum class Color{Red, Green, Blue};// 强类型不会隐式转int intmain(){Color cColor::Green;//if(c1){}// 错误不能直接跟int比if(cColor::Green){std::cout是绿色\n;}// 想转int必须显式转换 std::cout底层值: static_castint(c)\n;}16、C 类型转换四种转换一览关键字用途安全检查static_cast编译时能确定的安全转换编译时检查dynamic_cast运行时安全的向下转换运行时检查const_cast去掉const将常量转换为非常量常量转换只能用于转换掉 const 属性不能改变对象的类型编译时检查reinterpret_cast危险的底层位模式转换重新解释转换不同的数据类型之间进行转换无检查//dynamic_cast目标类型(表达式)intin1;float ftstatic_castfloat(in);//const_cast const int a12;int bconst_castint(a);
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。