C++ notebook

~1 库

写xx.h库时
#ifndef _xx_h
#define _xx_h


#endif _xx_h

//确定xx.h库一共只被调用一次,引用库相当于把库中的代码复制过来


~2 友元

class node{
private:
	int G1,G2;
	friend void work();//申明work函数是友元,可以调用node类里的私有变量
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
}G;
int work(){
	return G.G1+G.G2;
}



~3 运算符重载 (4种方法) (operator +)

method 1 (局部重载)
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}

	node operator +(const node b){return node(G1+b.G1,G2+b.G2);}
	/*
	node operator +(const node b)const {return node((*this).G1+b.G1,(*this).G2+b.G2);}
	//直接在里面调用G1,G2,相当于(*this).G1,(*this).G2
	*/
}G;

method 2 (局部重载)
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
	node operator+(const node b);
}G;
node node::operator +(const node b){return node(G1+b.G1,G2+b.G2);}
//相当于先定义要重载运算符,再在下面重载

method 3 (全局重载)
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
	friend node operator +(const node a,const node b);
}G;
node operator +(const node a,const node b){return node(a.G1+b.G1,a.G2+b.G2);}
//友元的作用是让重载时能调用到G1,G2,若是G1,G2在public中可以不用开友元

method 4 (全局重载)
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
	friend node operator +(const node a,const node b){return node(a.G1+b.G1,a.G2+b.G2);}
}G;
//相当于把method3的重载和开友元合并到一起来写,算是重载运算符的特殊写法

/*
全局重载 vs 局部重载
即 重载全局函数 vs 重载成员函数

2+node(2,3)时,全局重载会默认先把2类型转化成node(2),可以过编译
			而局部重载则过不了编译
*/



~4 重载运算符 += ++ >> << [] =

class node{
friend istream& operator >>(istream &in,node &G);//给它开友元
friend ostream& operator <<(ostream &out,const node &G);//给它开友元
//必须开全局变量
//可以合在一起写
private:
public:
	int G1,G2;
	int s[10],*p;
    node(){G1=0,G2=0,p=new int;}
    node(int _G1,int _G2){G1=_G1,G2=_G2,p=new int;}
	~node(){delete p;}
	node &operator +=(const node &b){
		(*this)=node(G1+b.G1,G2+b.G2);
		return *this;
	}
	// int下 a+=b <==> a=a+b <==> a加上b 并且 返回值为a

    int &operator[](int x){return s[x];}//函数名前加& 可以左值调用,相当于return后面的参数

	node &operator=(const node &x){
		if(this==&x)return *this;//一定要判=左右是否相等,不然下面delete指针会使代码出错
		delete p;
		p=new int;
		*p=*x.p;
		G1=x.G1,G2=x.G2;
		for(int i=0;i<10;i++)s[i]=x.s[i];
        return *this;
	}
	
	node& operator++(){//重载 前缀++
		G1++;
		return *this;
	}
	node operator++(int x){//x为形式参数,无意义,表示重载 后缀++
		node tmp;
        tmp=*this;
        //这里不能合写成node tmp=*this,因为这相当于直接构造tmp,用的是默认的赋值,写了node(node &G){}的构造函数才可以合写
		G1++;
		return tmp;
	}
	//函数名前加&,表示反回的是tmp的地址(左值)而不是右值
	//前缀++A,前缀--A,+=,-=函数名前面都要加&
	//后缀A++,后缀A--函数名前面不能加&
};
istream& operator >>(istream &in,node &G){//流读入
    // cout<<"####"<<endl;
    in>>G.G1>>G.G2;
    return in;
}
ostream& operator <<(ostream &out,const node &G){//流输出
    // cout<<G.G1<<" "<<G.G2<<endl;
    out<<G.G1<<"/"<<G.G2;
    return out;
}
//流输入时G前面不能加const,不然不能修改G了
//流输出时G前面加了const,则G传进来的可以是左值或右值,不然右值不能传进来,因为计算机可能认为G会被修改,但右值是不能被修改的


~5 类 函数写法

**
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
	int calc(){
		return G1+G2;
	}
}G;


**
class node{
private:
	int G1,G2;
public:
	node(int _G1=0,int _G2=0){G1=_G1,G2=_G2;}
	int calc();
}G;
int node::calc(){
	return G1+G2;
}

//以上两种写法等价,一般大工程题采用第二种先申明后定义的方式,看着更清晰(即函数的先申明后定义)


~6 类 构造函数和析构函数

//构造函数为生成类时自动调用的,析构函数为函数消亡时自动调用的
class node{
private:
	int G1,G2;
	int *p;
public:
	node(){G1=10,G2=2;}
	node(int _G1){G1=_G1,G2=1000;}
	
	
	node(int _G1,int _G2){G1=_G1,G2=_G2;}//以上三个都为构造函数,传入参数数量或类型不同
	//or
	node(int _G1,int _G2):G1(_G1),G2(_G2){}//先令G1=_G1,G2=_G2,在调用括号里的代码
	
	
	//如果没有自定义构造函数,系统将自动生成一个默认构造函数 node(){} ,所有元素一开始都为随机值
	//如果同时被匹配上多个同名函数/构造函数,则会报错
	~node(){delete p;}//如果类中存在指针,在程序运行过程中可能有p=new int,所以要把内存释放,不然会内存泄漏
	//如果没有自定义析构函数,系统将自动生成一个默认构造函数 ~node(){} ,表示什么也不做
}G;

~7 指针

int *p,*q;
int a=3,b[3];
p=&a;//取a的地址,p指向了a
q=b;//b其实记录的是数组b[3]的首地址,这里把b的地址赋值给了q,相当于可以用q调用b数组,q[i]与b[i]相同

int *p=new int;//动态申请内存
delete p;
int *q=new int[10]//申请了一个数组,q指向其首地址,可以把q[i]当成数组看
delete[] q;
//指针指向下一个地址或程序结束时,一定要把空间回收掉,不然会内存泄漏


int *p[10];//指针数组
for(int i=0;i<10;i++)p[i]=new int[10];
for(int i=0;i<10;i++)for(int j=0;j<10;j++)p[i][j]=1;
for(int i=0;i<10;i++)delete[] p[i];
//申请二维数组


int **q;//多级指针
q=new int*[10];
for(int i=0;i<10;i++)q[i]=new int[10];
for(int i=0;i<10;i++)delete[] q[i];
delete[] q;
//申请二维数组


int &j=i;//j可以引用i,即j成为了i的别名,调用j就相当于调用i
//定义引用类型的变量时必须立即对它初始化,不能写成 int &j;j=i

void F(int x,int y){
	cout<<"!!!"<<x<<" "<<y<<endl;
}
int main(){
	void (*p)(int,int);//函数指针 void表示函数返回类型,(int,int)表示传入参数类型
	p=F;//表示p指向F函数的入口地址,即以下可用p代替F
	p(2,3);
	return 0;
}


const int *p=new int;//不能改*p的值
int *const p=new int;//不能改p指向的地址



~8 template模板

template<class T>
T Add(T a,T b){return a+b;}

template<typename T>//这里class和typename等价,但建议写typename以和类class区分
T Add(T a,T b){return a+b;}

template<typename T>
class node{
public:
	int x,*p;
	T G;
	T Work(T x,T y){
		return x+y;
	}
};
int main(){
	node<int> A;//一定要申明T的类型(此处为int)
	node<int> *B=&A;
	A.x=1;
	B->x=1;//这两种用法等价,单纯的node类型用".",指针用"->"
	// !!!   B->x就是(*B).x
	
	A.p=new int;
	B->x=new int;//二者等价

	*(A.p)=3;
	*(B->x)=3;//二者等价,*放最外面
	return 0;
}



~9 可变参数模板 ...

#include<bits/stdc++.h>
using namespace std;
void debugPrint(){cout<<endl;}//最后还要写一个传入变量数为0的函数
template<typename T,typename ...T2>
void debugPrint(T a,T2... b){//T2... b表示传入多个变量(变量数为>=0的任意个)
	cout<<a<<" ";
	debugPrint(b...);
}
int main(){
	std::string str("test message");
	debugPrint("Hello, Happy World!", 19260817, false, str);
	debugPrint("debug");
	return 0;
}

~10 main函数传参数

int main(int agrc,char *agrv[]){
	cout<<agrc<<endl;
	for(int i=0;i<agr;i++)cout<<agrv[i]<<" ";cout<<endl;
	return 0;
}
//传参进去的变量名称并不是限制死为agrc和 *agrv[],也可以改成如以下的写法
int main(int num,char *nmdp[]){
	cout<<num<<endl;
	for(int i=0;i<agr;i++)cout<<nmdp[i]<<" ";cout<<endl;
	return 0;
}
/*
命令行输入
./a 233 abc

程序输出
3
./a 233 abc
*/



~11 Lambda表达式 最简单应用
bool cmp(pp x,pp y){return x.fi+x.se<y.fi+y.se;}
int main(){
	sort(a+1,a+n+1,cmp);
}

可改为

int main(){
	sort(a+1,a+n+1,[](pp x,pp y)->bool{return x.fi+x.se<y.fi+y.se;});
}
//[]内先不考虑,()内表示传入参数,bool表示函数返回值,{}内为函数主体
//像sort一样很多系统自带函数支持Lambda表达式

int a[5],b=3;
auto func = [=](int x,int y)->int {return x+y+b+a[1];};
cout<<func(a[2],a[1])<<endl;
/*
//相当于把该Lambda表达式存到func里,func必须为auto类型
Lambda表达式可以捕获Lambda可见范围内的所有变量
[=]       表示捕获的变量为值传入
[&]       表示捕获的变量为地址传入
[&a]      表示捕获变量a用地址传入
[]        表示不能捕获变量
[=,&b,&c] 表示捕获变量b,c时地址传入,其他为捕获其他变量为值传入
[&,b,c] 表示捕获变量b,c时值传入,其他为捕获其他变量为地址传入
若为值传入,捕获的值为Lambda创建时捕获的值,而非使用时再次去捕获
*/

~12 && 右值引用

/*
值- 左值
 \
   右值- 纯右值
       \
		 将亡值
*/

class node{
public:
	int *p;
	node(){}
	node(node &&_G){p=_G.p;_G.p=nullptr;}
	/*
	&& 表示传入右值(这段代码下面传入的是将亡值)
	右值自身不能被调用地址,即不能写成*this=G这种形式,所以主要一般是用在 直接把其内部指针的地址复制过来
	//由于是将亡值,所以在复制地址完之后 应该在它消亡前把动态指针地址改成nullptr,防止其被delete掉
	右值引用的目的主要是为了是减少内存拷贝,优化性能
	*/
};
node Fun(){
	node S;
	S.p=new int[10];
	for(int i=0;i<10;i++)S.p[i]=i;
	return S;
}//此处返回的是将亡值
int main(){
	node G(move(Fun()));//给G赋初值时传入的是一个右值(将亡值)   move(x)表示将x强行转换成右值
	for(int i=0;i<10;i++)cout<<G.p[i]<<" ";
	cout<<endl;
	return 0;
}
/*
输出结果为:
0 1 2 3 4 5 6 7 8 9
*/


~13 组合和继承

//组合--将point当成line的成员函数
class point{
	int x,y;
};
class line{
	point a,b; //point a,b被称为对象成员
};

//继承--将point的代码继承到point2中,继续新添加功能
class node{
private:
    int a;
protected:
    int b;
public:
    int c;
}
class node2:public node{//public继承

};

//private:只能被当前类中的成员函数访问
//protested:只能被当前类和派生类中的成员函数访问
//public:可以被所有函数访问

//private继承:将基类的public类型变成private类型,基类的protected类型仍为private类型(相当于与private取min)
//protected继承:将基类的public类型变成protected类型后继承(相当于与protected取min)
//public继承:完全继承基类(相当于与public取min)
//ps:无论是哪种继承方法,基类中的private都不能被继承

//派生类调用构造函数时:先调用基类的构造函数,再调用对象成员(如果存在)的构造函数,最后调用自己的构造函数
//派生类调用析构函数时:先调用自己的析构函数,再调用对象成员(如果存在)的析构函数,最后调用基类的析构函数


class node{
public:
    int a;
    int b;
    int c;
    node(int _a=0,int _b=0,int _c=0):a(_a),b(_b),c(_c){cout<<"new node   "<<a<<" "<<b<<" "<<c<<endl;}
	~node(){cout<<"end node"<<endl;}
	int F(){return a;}
	node &operator=(const node &other){
		if(this==&other)return *this;
		a=other.a,b=other.b,c=other.c;
		return *this;
	}
};
class node__{
public:
    int a;
    node__(){cout<<"new node__"<<endl;}
	~node__(){cout<<"end node__"<<endl;}
};
class node2:public node{
public:
    int G;
	node__ A;
    node2():node(1,2,3){cout<<"new node2"<<endl;G=4;}//先调用node(1,2,3),再调用node__的构造函数,最后调用括号里的
	or
	node2(){cout<<"new node2"<<endl;G=4;}//等价于 node2():node(){cout<<"new node2"<<endl;G=4;}  调用node()默认构造函数

	~node2(){cout<<"end node2"<<endl;}//先调用括号里的,再调用node__的析构函数,最后调用node的析构函数
	int F(){return G;}//此函数F()与基类中F()名称与调用类型完全相同,此时认为是派生类里的新函数覆盖了基类里的旧函数
	node2 &operator=(const node2 &other){
		if(this==&other)return *this;
		G=other.G,A=other.A;
		node::operator=(other);
		//调用基类的赋值函数,尽管other是个node2类型,但这样调用会把other的基类传进去(C++规定派生类函数能自动转化成基类函数)
		return *this;
	}
};
/*
调用node2 A; 输出如下

new node   1 2 3
new node__
new node2
end node2
end node__
end node
or

new node   0 0 0
new node__
new node2
end node2
end node__
end node
*/




~14 多态性、虚函数、纯虚函数、抽象类

//编译时的多态性
int f(){return 1;}
int f(int x){return 2;}

//编译时的多态性
int operator+(int a,int b){return a+b;}
int operator+(pp a,pp b){return pp(a.fi+b.fi,a.se+b.se);}


//运行时的多态性
class Shape{
public:
    virtual double S(){return 233;}//前面加virtual,定义虚函数
	virtual ~Shape(){}
	/*析构函数前面务必加上virtual(一定要写带virtual的析构函数)
	加上virtual后,一个基类的指针指向派生类,其消亡时本来只会调用基类的消亡函数
	但基类的消亡函数前面如果带了virtual,它会先去调用派生类的消亡函数,再来调用基类的函数
	*/
};
class Circle:public Shape{
public:
    double r=1;
    virtual double S(){return pi*r*r;}//此处前面virtual可以不加
};
class Rectangle:public Shape{
public:
    double a=2,b=3;
    virtual double S(){return a*b;}//此处前面virtual可以不加
};
int main(){
    Circle A;
    Rectangle B;
    Shape *p=&A,*q=&B;
    cout<<p->S()<<" "<<q->S()<<endl;
	/*
	本来p是一个Shape的指针,其指向派生类,只能调用派生类的基类的成员
	但S()变为虚函数后,将先在派生类中找同名函数(名称和参数类型都相同),若存在则调用派生类中的函数,找不到则调用原函数
    */

	Shape *t=new Circle;
	delete t;
	/*
	由于Shape的析构函数前面带了virtual,此时先调用Circle的析构函数,再调用Shape的基类函数
	Circle的析构函数会把new Circle开的Circle类中的a,b的空间释放掉
	所以用类来写多态时,基类函数前面一定要带virtual
	*/
	return 0;
}

/*
输出为 : 3.14159 6

若将virtual去掉,输出为: 233 233
*/

virtual double S(){return 233;}//虚函数
virtual double S()=0;//纯虚函数
/*
将上面改成下面后上述代码也成立
区别:如果一个函数具有至少一个纯虚函数,则其被称为抽象类,抽象类可以创建指针(如Shape *p),但不能创建该类对象(如Shape C)
     因为类中有纯虚函数,其为未定义完全的函数
*/




~15 文件输入输出
ofstream out("1.in");//另一个名为out的输出文件流和1.in相关联,此时会自动先清空1.in(若1.in不存在会新建一个)
out<<1<<" "<<2<<endl;//将1 2写入到out缓冲区,endl会自动将当前缓冲区的内容读入到1.in中
out.close();//关闭out和1.in文件的关联,并将out缓冲区剩下的内容全部读写入1.in
out.open("2.in")//可以令out再与2.in相关联
...
out.close();

ifstream in("1.in");//另一个名为in的输入文件流和1.in相关联(若1.in不存在不会新建)
int a,b;
in>>a>>b;//从1.in中读入
cout<<a<<" "<<b<<endl;
in.close();	//关闭out和1.in文件的关联

fstream io("1.in");//创建一个名为io的输入输出文件流,令其与1.in相关联
in.seekp(4);//找到1.in中第4个字节的位置,从这里开始读入或更改(0-base)   ( 默认为从头开始,即in.seekp(0) )
int a,b;
in>>a>>b;//依次读入两个整数
in<<a<<b;//接着写入两个整数,直接将1.in中相应位置给替换掉,Ascii文件存储 233333算6个字符类型,而不是一个整数类型,占6个字节
io.close();

fstream io("1.in",fstream::in);//io相当于一个ifstream类型
fstream io("1.in",fstream::out);//io相当于一个ofstream类型

//判断文件是否读完:
while(in>>x){}
while((ch=in.get())!=EOF){}//读接下来一个字符放到ch中,ch为char
while(!in.eof()){in.read(ch,5);}//读入接下来5字节放到x中,ch为char*


ofstream out("1.in");
for(int i=1;i<=10;i++)out.write(reinterpret_cast<const char*>(&i),sizeof(int));//表示用1.in中数据用二进制文件存储(这里一个i占4字节)
out.close();
fstream in("1.in");
int x;
in.read(reinterpret_cast<char*>(&x),sizeof(int));//从二进制文件中读取数据
while(!in.eof()){
	cout<<x<<" ";
	in.read(reinterpret_cast<char*>(&x),sizeof(int));//从二进制文件中读取数据
}
in.close();

//采用二进制文件存储,可方便的用fstram类型定向修改某一位置的值



~16 函数指针
int calc(int a,int b){return a+b;}
int (*p1)(int,int); //返回类型(*指针变量)(形式参数表)
p1=calc;//让p1指向名为calc的函数
cout<<p1(3,4)<<endl;//等价于调用calc(3,4)
//or
cout<<(*p1)(3,4)<<endl;//等价于调用calc(3,4)



~17 模板的特定实例的友元

template<typename T1>class node;
template<typename T1>
class node2{
	template<typename T1> friend class node;//所有node类模板都为友元
	friend class node<T1>;//申明只有当node类 模板为int类型的特定实例 才为友元
};




~18 异常处理 try throw catch

class Error1{//定义一个异常类Error1
public:
    void Output(){cout<<"error1"<<endl;}
};
class Error2{//定义一个异常类Error2
public:
    void Output(){cout<<"error2"<<endl;}
};
class Error3{//定义一个异常类Error3
public:
    void Output(){cout<<"error3"<<endl;}
};
void G(){
    throw Error3();//异常抛出,此处零时用构造函数构造一个Error3类的对象,将它抛出
}
int main(){
    try{
        try{
            cout<<"1"<<endl;
            G();//此处已经throw,直接跳出当前try下面第一个捕获到它的catch,并调用当前已定义变量的析构函数
            throw Error1();
            throw Error2();
            cout<<"2"<<endl;
        }
		//try后面要紧跟catch,中间不能有其他代码
        catch(Error1 tmp){//此处未捕获到,继续跳出(若是不需要用到tmp,也可只定义要捕获的类型Error1)
            tmp.Output();
        }
    }
    catch(Error2 tmp){//未捕获到,继续向下
        tmp.Output();
    }
    catch(...){//捕获剩余所有抛出的异常
        cout<<"other Error"<<endl;
    }
    cout<<"3"<<endl;
    return 0;
}

/*
输出:
1
other Error
3
*/



~19 静态成员与静态成员函数(例外:静态常量成员) static

class A{
private:
    int x;
    static int rate;//开了一个静态成员,为类A的所有对象共享
    static const int GGG=20;//开了一个静态常量成员,每个对象单独开,必须在类定义时直接初始化
public:
    A(int _x=0):x(_x){}
    static void Set_rate(int x){rate=x;}//静态成员函数,用来访问静态成员(或其他静态成员函数)
    int Get(){return x*rate;}
};
int A::rate=2;//为该静态成员申请空间,需要且仅需要申请一次
//静态常量成员是例外,不需要申请空间,其储存在类的对象中
int main(){
    A a(2),b(3),c(4);
    A::Set_rate(3);//调用类A中的静态成员函数,以修改静态成员rate
    cout<<a.Get()<<endl;
    cout<<b.Get()<<endl;
    cout<<c.Get()<<endl;
    return 0;
}

/*
输出:
6
9
12
*/



~more

/*
C++中,赋值是一种运算,返回值是左边对象
类是更合理的库
类没定义private还是public,默认是private
delete对象可以为空指针
不能delete一个地址两次
空指针p不能调用*p(若为node *p,p=nullptr,p不能调用node中的变量)
判断p!=nullptr常数极大(事实如此)


int Get(){return a.x;}
friend bool operator==(const Calendar& a,const Calendar& b){return a.Get() == b.Get();}
可能过不了编译,因为a,b前面加了const,而系统认为a.Get()可能会改变a,b的内部变量
所以应该再改成
int Get()const{return a.x;}//加上const

size_t 为unsigned int(32位机子)/unsigned long long(64位机子)
在类里面,函数可以直接调用在它下面写的函数,而不用在前面先定义


to_string(x) 将整数/浮点数类型的x转化成string

class A{
public:
	class B{

	};
};
B叫做嵌套类,和A没有任何关系(即B中元素不能调用A中private的元素),只是A可以调用它。如果class B定义在public中,相当于定义在外面
*/

~more more

/*
1.
0开头为8进制
0x开头为16进制

2.
cout<<"1as\023";读到\0为止
cin>>s;//char *s 读入最后会加入\0

3.
特定只能重载成成员函数不能重载成友元函数的4种操作符: =,(),[],->
特定只能重载成友元函数不能重载成成员函数的操作符: <<,>>

4.不能解引用空指针

5.
char s[10];
sizeof(s) =10 数组所占字节大小
char *s=new char[10];
sizeof(s) =4/8 指针所占字节大小(因为计算机不确定新开空间的具体大小)

strlen(s) 往后数到\0为止

6.
int const *p=new int;
const int *p=new int;
int *const p=new int;
前两种相同指针不能修改指向地址的值
第三种指针不能修改指向地址

7.
程序代码不能有二义性
尤其注意隐式转换

8.
cin.getline(s,10,'.');
cin.get(s,10,'.');
区别为get读到'.'结束后,'.'仍存在于输入流中
而getline读到'.'结束后,结束符'.'从输⼊流中被删除

9.
cout<<"\141"; 即输出a (97=64*1+8*4+1,8进制)
cout<<"\60"<<"\060"; 即输出两个0(48=8*6,8进制)
cout<<"\t"; 即输出tab

10.
int *p=new char[10] 动态开出的数组最后要手动加上\0

11.
class node{
    int a,b;
public:
    node(int _a=0,int _b=0):a(_a),b(_b){}
    operator int() const{return a+b;}//类型转换,可用int(G)把G从node类型转化成int类型
};

12.
隐函数转换时不能存在二义性
class node{
    int a,b;
public:
    node(int _a,int _b):a(_a),b(_b){}//普通构造函数
	node(const node &tmp){a=tmp.a,b=tmp.b;}//拷贝构造函数
    node(int x=0){a=x,b=x;}//转换构造函数
    node operator +(const node x)const{
        node tmp(a+x.a,b+x.b);
        return tmp;
    }
    operator int() const{return a+b;}//类型转换函数
};
int main(){
    node G(3,4);
    int x=2;
    G+x;//这里有两种可行的隐函数转换,要么将G从node转成int,要么从将x从int转成node
	//从而这里会编译报错
    return 0;
}

13.
同时编译多个文件
g++ a.cpp b.cpp -o a

注意:a.cpp和b.cpp中只能有一个main函数
某一源文件中变量/函数/类前面加了static,则该变量/函数/类只能在该源文件中使用
不然,可以通过在另一源文件中加形如extern int a;/extern void init(); 的代码使得另一源文件也可以调用该变量/函数
不过extern的变量只能在其他源文件中一共定义一次(不算static)
ps:申明函数和类前面可以不加extern,加/不加是等价的

使用#include某一个库,相当于手动把源文件链接起来(显然不能相互引用形成环),最后形成森林一样的结构

14.
内联函数不能递归

15.
尾置返回

void init(){}
可以写成
auto init()->void{}
//使用尾置返回时前面要带个auto

lambda表达式要使用尾置返回


16.
class node{

public:
	void calc(const node a)const{//前一个const表示该函数不能修改a,后一个const表示cosnnt不能修改*this

	}
}


17.
void calc(const int &x){}

calc(3);//可以这样调用,可以当作给3这个常量开了个静态存储区,调用时虽然取地址了,但前面有个const,保证了其不会被修改


18.
class node{
	static const int x=2;//直接在类里面赋初值
	static int y;
	const int z;//可以选择不直接赋初值,而是在构造函数的初始化列表中赋初值
	node():z(3){}
};
int node::y=2;//要在外面定义+赋初值

19.
explicit:构造函数不能使用必须为显示转换,不能为隐式转换

class node{
private:
    int a;
public:
    explicit node(int _a=0):a(_a){}
};
int main(){
    node A;
    A=2;//编译报错,因为这里用到了int到node的隐式转换,但上面定义时写了explicit,要求必须为显示转换
	A=node(2);//正确
    return 0;
}


20.
int a=22;//10进制
int a=0x22;//0x开头为16进制
int a=022;//0开头为8进制

21.
char s[]="abb";//相当于开了个s[4],s="abb\0"

22.
char中'\0'的Ascii码为0

23.
strcpy(a,b) 拷贝到\0为止,\0也会被拷贝过去

24.
void solve(base x){//此处调用拷贝构造函数,而不是赋值"="  (若没有定义拷贝构造函数,系统会产生一个默认的拷贝构造函数)
    return;
}
int main(){
    base x;
    solve(x);
    return 0;
}

25.
cout<<setw(8)<<setfill('#')<<endl;//setw下一次有效,setfill永久有效



*/
posted @ 2022-03-31 11:40  zhongzero  阅读(39)  评论(0编辑  收藏  举报