标签:总结 const 函数 int void C++ 学习 NULL 指针
(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修饰时,可以修改;
标签:总结,const,函数,int,void,C++,学习,NULL,指针 来源: https://blog.csdn.net/dongxianfei/article/details/120025169
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。