C++学习总结3
2021/9/4 17:07:22
本文主要是介绍C++学习总结3,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
(1)C预处理,条件编译
(A)宏定义与宏替换
宏名一般大写,替换发生在编译之前,且是机械替换,不做语法检查,也不分配内存,不占用运行时间,只占用编译时间。
1、符号常量的宏定义和宏替换 :#define 标识符 字符串
#include<iostream> #define P 3+4 using namespace std; void main() { int a=2; cout<<a*P<<endl; //相当于a*3+4,而不是a*(3+4),机械替换 }
2、带有参数的宏定义和宏替换:#define 标识符(参数列表) 字符串
#include<iostream> #define FUN(a) a*a using namespace std; void main() { cout<<FUN(2+3)<<endl; //机械替换,2+3*2+3 }
(B)文件包含
#include的作用是把它后面所写的那个文件的内容,一字不改地包含到当前的文件中。
- #include <filename> 认为该文件是标准头文件,先到标准库中寻找,若没有,再到当前目录下寻找;
- #include"filename" 一般认为是用户自定义文件,先到当前目录寻找,若没有,再到类库中寻找;
(C)条件编译
//格式 #if/ifdef/ifndef #elif #else #endif
一般用在头文件中,避免多重包含,在源程序中引入头文件,相当于把头文件的内容复制到源文件当中。
我们都知道,一个符号可以多次声明,但只能定义一次。那么头文件的引用就涉及到了这个问题,当一个函数fun()在头文件source中进行定义后,若类A中包含该文件,类B也包含该文件,而在源文件C中用到了A和B,#include"A" 和#include"B"时,source在C中包含了两次,会出现fun()的多次定义,导致错误。
#ifndef S #define S //类的定义 #endif
(2)_cpluscplus
_cpluscplus是c++中的定义,而c中没有该定义
- 用来判定代码是c类型还是c++类型
- cplusplus的类型是"long int",值为199711L
int main() { #ifdef _cplusplus printf("This is c++ program"); #else printf("This is c program"); }
(3)NULL和nullptr
(A)
NULL是一个宏定义,在c和c++中的定义不同,c中NULL为(void*)0,而c++中NULL为整数0
//C语言中NULL定义 #define NULL (void*)0 //c语言中NULL为void类型的指针,但允许将NULL定义为0 //c++中NULL的定义 #ifndef NULL #ifdef _cpluscplus //用于判定是c++类型还是c类型 #define NULL 0 //c++中将NULL定义为整数0 #else #define NULL ((void*)0) //c语言中NULL为void类型的指针 #endif #endif
所以在C++中int *p=NULL; 实际表示将指针P的值赋为0,而c++中当一个指针的值为0时,认为指针为空指针。
(B)
nullptr是c++11中的关键字,表示空指针,是一个字面值常量,类型为std::nullptr_t,空指针常数可以转换为任意类型的指针类型。
在c++中(void *)不能转化为任意类型的指针,即 int *p=(void*)是错误的,但int *p=nullptr是正确的。
void fun(int i){cout<<"1";}; void fun(char *p){cout<<"2";}; int main() { fun(NULL); //输出1,c++中NULL为整数0 fun(nullptr);//输出2,nullptr为空指针常量,是指针类型 }
(4)C++静态成员
静态数据成员的类型可以是它所属类的类型,而非静态数据成员则受到限制,只能声明为它所属类的指针或引用。
class type { static type a; //ok,静态成员可以是不完全类型 type *b; //ok,指针成员可以是不完全类型 // type c; //error,数据成员必须是完全类型 public: //... };
静态成员可以作为默认实参,而非静态成员不能。
class student { static int a; public: void fun(int b=a); }
- 类的静态成员函数不能被声明为const,因为const表明该成员函数不会修改该成员函数所属对象,而static成员函数不属于任何对象;
- static也不能被声明为虚函数,涉及到父子类对象;
- 不能被声明为volatile;
(5)C++引用
C++中规定,一旦定义引用就必须进行初始化,且不能在再被指定为其他变量的引用。
int a=3,b=1; int &ref=a; //ok,ref是a的引用 ref=b; //将b的值赋给a,而不是将ref绑定到变量b int &ref; ref=a; //error,必须在定义时初始化
引用和指针的区别:
- 引用不能为空,创建时必须初始化(作为类的数据成员时除外,需要用初始化列表的方式初始化)。指针可以为空,可以任何时候被初始化;
- 一旦一个引用被初始化为指向一个对象,它就不能再被改变为另一对象的引用。指针则可以随时指向另一对象;
- 没有NULL引用。可以NULL指针;
- 如果返回动态分配的对象或内存,必须使用指针。引用可能引起内存泄漏。(无法使用delete+引用方式释放内存)
- 给引用赋值,改变的是引用绑定的变量的值,而不是使引用与另一对象相关联;
(6)mutable和volatile关键字
(A)mutable
在C++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改。mutable在类中只能够修饰非静态数据成员。
#include <iostream> using namespace std; class test { mutable int a; int b; public: test(int _a,int _b) :a(_a),b(_b){}; void fun() const //fun是const 函数,不能修改类的对象的数据成员,但由于a被mutable修饰,可以修改,但不能修改b { a += b; } void print() { cout << a << "," << b << endl; } };
如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutable来修饰。
(B)volatile
volatile应该解释为“直接存取原始内存地址”比较合适,一般常用于多任务环境下各任务间共享的标志应该加volatile。
(7)final 和 override
(A)final
有时我们会定义这样一种类,我们不希望其他类继承它。C++ 11新标准提供了一种防止继承的方法,即在类名后跟一个关键字final。
class base final {/* */} //base不能作为基类 class Derived:base { /* */} //错误,base不能作为基类
此外,final还可以修饰类中的虚函数,表示类成员函数不可以在派生类中进行覆盖。
class base { virtual void fun() final {...}; } class derived:base { void fun(){...}; //error }
(B)override
对于基类中的虚函数,派生类可以选择覆盖或者不覆盖,对于选择覆盖的函数用override加以修饰,表示该函数覆盖了基类的虚函数。
class base { public: virtual void f1(); virtual void f2(); void f3(); } class derived:public base { public: void f1()override; //ok void f2(int) override; //error,基类中不存在f2(int)虚函数 void f3() override; //error,f3不是虚函数 }
注:final,override修饰的函数必须为虚函数。
(8)拷贝构造函数必须是一个引用
拷贝构造函数的参数必须是引用,参数传递的方式有两种,值传递和地址传递。
其中值传递即是拷贝原对象的一个副本作为实参,即参数传递的过程中也调用了拷贝构造函数,若拷贝构造函数的参数不是引用的话,会造成无穷递归的调用拷贝构造函数。
而引用是直接操作原对象,因此不会出现上述问题。
(9)模板的特化
模板分为类模板和函数模板,特化分为全特化和偏特化。
- 全特化:给模板中的所有模板参数指定一个具体的类;
- 偏特化:部分指定模板参数的类;
- 类模板可以全特化也可以偏特化;
- 函数模板只能全特化;
//类模板 template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;} private: T1 a; T2 b; }; template<> class Test<int , char> { public: Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;} private: int a; char b; }; template <typename T2> class Test<char, T2> { public: Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;} private: char a; T2 b; };
//函数模板 template<typename T1, typename T2> void fun(T1 a , T2 b) { cout<<"模板函数"<<endl; } //全特化 template<> void fun<int ,char >(int a, char b) { cout<<"全特化"<<endl; } //函数不存在偏特化:下面的代码是错误的 /* template<typename T2> void fun<char,T2>(char a, T2 b) { cout<<"偏特化"<<endl; } */
(10)const修饰符
(A)指向const常量的指针 和 const指针
//(1)指向const常量的指针:const int *p 或 int const *p //p是一个指针,指向const int类型的常量;指针指向的内容为常量,因此不能改变*p的值,但指针p可以改变 const int a=2; const int b=3; const int *p=&a; *p=4; //error,p指向常量a,不能修改 p=&b; //ok,p只要指向const int类型即可 //(2)const指针:int *const p //p是一个指针,指针p的值不能改变,但其指向的值可以改变 int a=2; int b=3; int *const p=&a; *p=4; //ok,p的内容可以改变 p=&b; //error,p是常指针,指针值不能修改 //(3)指向const常量的const指针:const int *const p 或 int const *const p //p是一个const指针,指针值和指向的对象的值都不允许修改 int a=2; int b=3; const int *const p=&a; *p=4; //error,p指向常量 p=&b; //error,p是常指针
(B)类的const成员
类的const成员包括const数据成员和const成员函数;
(1)const数据成员:和普通的const变量一样,定义时初始化,且不能修改;
(2)const成员函数:
- const成员函数只能访问其他的const成员函数,而不能访问非const成员函数;
- const可以修饰static数据成员,在定义时初始化,但仍要在类外进行声明;
- const不能修饰static成员函数,因为const表示不修改类的对象,而static成员函数属于类,不涉及到对象;
- const成员函数不能修改类的对象,即不能修改数据成员,但当数据成员被mutable修饰时,可以修改;
这篇关于C++学习总结3的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-10Rakuten 乐天积分系统从 Cassandra 到 TiDB 的选型与实战
- 2025-01-09CMS内容管理系统是什么?如何选择适合你的平台?
- 2025-01-08CCPM如何缩短项目周期并降低风险?
- 2025-01-08Omnivore 替代品 Readeck 安装与使用教程
- 2025-01-07Cursor 收费太贵?3分钟教你接入超低价 DeepSeek-V3,代码质量逼近 Claude 3.5
- 2025-01-06PingCAP 连续两年入选 Gartner 云数据库管理系统魔力象限“荣誉提及”
- 2025-01-05Easysearch 可搜索快照功能,看这篇就够了
- 2025-01-04BOT+EPC模式在基础设施项目中的应用与优势
- 2025-01-03用LangChain构建会检索和搜索的智能聊天机器人指南
- 2025-01-03图像文字理解,OCR、大模型还是多模态模型?PalliGema2在QLoRA技术上的微调与应用