C++基础

C++ 1day

类型增强

//hello world程序
#include<iostream>
using namespace std;   
int main()
{
cout << "hello world" << endl;  //相当于C的printf
return 0;
}

C++对类型的检测更为严格 如:malloc的使用

再比如const

#include <iostream>
using namespace std;
int main()
{
const int a = 100;
int *q = &a;      
//这是不能通过编译,因为 &a的类型为 const int *,而 q 是int*类型不同C++报错
}
真正的枚举
#include<iosrteam>
using namespace std;
enum Season
{
Spring,Summer,Fall,Winter
};
int main()
{
	Season s = Spring; 
	//这是可以的,也可以在Season前加上enum,但是右值不可以是除枚举类型Season以外的,则才是真正的枚举类型,所谓枚举类型就是先给出来在利用,只能利用举出来的
}
字符串类型
//字符串类型
int main()
{    
string s;  //遇到空格结束      
cin >> s;    
cout << s << endl;    
return 0;
}
终端输入:abc abc
终端输出:abc

int main()
{    
char s[1024];    
cin >> s;    
cout << s << endl;    
return 0;}
终端输入:abc abc
终端输出:abc
函数同名不同参也就是重载
#include <iostream>
using namespace std;
void func(int a)
{
	printf("%d\n", a);
}
void func(int* p)
{
	printf("%d", p);
}
int main()
{
	func(1);        //类型本身就是 int
	func((int*)1);  //将类型转化为 Int *
	func(NULL);	    //NULL是int类型的空
	func(nullptr);  //nullptr是指针类型的空
	return 0;
}
终端输出:
1##
1
0##
0

运算符的重载也是函数的重载 <<

#include <iostream>
using namespace std;
struct COMP
{
	float real;
	float image;
};
COMP operator+(COMP one, COMP another)  
{
	one.real += another.real;
	one.image += another.image;
	return one;
}
int main()
{
	COMP c1 = { 1,2 };
	COMP c2 = { 3,4 };
	
	COMP sum = c1 + c2;      //返回一个结构体,结构体之间可以进行赋值运算
	cout << sum.real << " " << sum.image << endl;
	return 0;
}
//构造类型是不能比较的所以使用操作符的重载来达到目的
#include <iostream>
using namespace std;
typedef struct _pos
{
	int x_; int y_;
}Pos;

bool operator == (Pos one, Pos another)
//operator 关键字,对操作符进行重载
{
	if (one.x_ == another.x_ && one.y_ == another.y_)
	return true;
	else 
	return false;
}
int main()
{
	Pos ps = { 1,2 };
	Pos fd = { 3,4 };
	if (ps == fd)
		cout << "==" << endl;
	else
		cout << "!=" << endl;
	return 0;
}

函数重载的规制:

  1. 函数名相同。
  2. 参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
  3. 返回值类型不同则不可以构成重载。(无法通过编译) //anbiguous (二义性)

C++的输入输出

#include <iostreeam>
using namespace;
int main()
{
	int a,b,c;
	cin>>a>>b>>c; //往a,b,c内输入数据  
	//cin表示流输入运算
	cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
	return 0;
}
关于进制的输出
#include <iostream>
using namespace std;
int main()
{
	int data = 1234;
	cout << data << endl;
	cout << hex << data << endl;
	cout << oct << data << endl;
	cout << data << endl;
	cout << dec << data << endl;
	return 0;
}
终端输出
1234    第一次默认是十进制
4d2		输出十六进制
2322	输出八进制
2322	这种写法与第一种相同但是结果不同,是因上回输出了八进制,进行了保留
1234	
域宽,对齐,填充
#include <iostream>
#include<iomanip>   //manipulate  操纵
using namespace std;
int main()
{
	float f = 1.234;
	cout << f << endl;
	cout << setw(10) << f << endl;// 将域宽设为10
	cout << "***" << setw(10) << setiosflags(ios::left) << f << "**" << endl;
	//设置对齐方式setiosflags(ios::left),默认是右对齐
	cout << setw(10) << setfill('+') << f <<endl; setfill(字符)设置填充字符
	
	return 0;
}
终端输出
1.234
     1.234
***1.234     **
1.234+++++

默认参数

规则:

  1. 默认的顺序,是从右向左,不能跳跃。
  2. 函数声明和定义一体时,默认认参数在定义(声明)处。声明在前,定义在后,默认参 数在声明处。
  3. 默认值可以是常量,全局变量,或是一个函数
  4. 冲突 一个函数,不能既作重载,又作默认参数的函数。当你少写一个参数时,系统无法 确认是重载还是默认参数。(当实现功能,既能用默认参数又可以使用重载时,推荐使用默认参数)
#include<iostream>
using namespace std;
void func(int a){}
void func(int a,int b=2){}
int main()
{
	func(1,5)  //很明显是在调用第二个函数
	func(4)    //这就说不清楚了,可以理解为调用第一个函数,也可以理解为调用第二个函数,第二个参数的默认值为2
}

Reference &引用

定义:

变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个 别名。

  1. 引用是一种声明关系,不开辟空间
  2. 引用必须要初始化,不能单独存在
  3. 与被别名的变量拥有相同的数据类型
  4. 声明关系一旦确立不可变更
  5. 可以对引用再次引用,也就是对一个变量可以进行建立多个引用,此时引用之间是平行关系
int a = 100;
int b = 20;
int &ra = a;   //此时ra与a具有等价关系
int &rb = a;   //c此时rb与ra是等价关系
int &ra = b;   //违反了声明关系一旦确立不可变更
应用

c++很少使用独立变量的引用,如果使用某一个变量就直接使用他的二原名,没有必要使用他的别名。引用的目的就是取代指针传参,c++引入引用后,可以用引用解决的问题,就不用用指针解决了

引用从宏观上可以理解为,扩展了变量的作用域,传参后就像再本地解决问题一样。避免了传n级指针,解决n-1级问题,即平级解决问题

#include <iostream>
using namespace std;
void myswag(int &a, int &b)
{
	int t = a;
	a = b;
	b = t;
}
int main()
{
	int a = 1;
	int b = 5;
	myswag(a, b);
	cout << "a = " << a << endl << "b = " << b << endl;
	return 0;
}
终端输出
a = 5
b = 1
引用的提高
指针的引用(int *&)有,引用的指针(int& *)无
#include <iostream>
using namespace std;
void myswag(char *& p,char *&q)
{
	char * t = p;
	p = q;
	q = t;
}
int main()
{
	char* p = "china";
	char* q = "amar";
	cout << "p = " << p<< "  q = " << q <<  endl;
	myswag(p, q);
	cout << "p = " << p <<  "   q = " << q << endl;
	return 0;
}
终端输出
p = china  q = amar
p = amar   q = china

引用的本质是对指针的包装,避免使用裸露的指针

int main()
{
	int a;
	int *p = &a;    //指针p指向a
	int & ra = a    //对a进行引用
	int *&pr  = p;  //对指针变量p进行引用,p是int*类型
	int& * = &ra;   //(不被允许)ra是int&类型,取地址后为int&*类型
	//但是这种操作是不被允许的,引用就是对指针的包装,但是*又会解引用,不被允许
	//对引用的指针类型 C++避免再次开封
}
指针的指针(int **)有,引用的引用(int &&)无,可以对引用再次取引用
int main() //指针的指针有
{ 
	int a = 100;
	int *p = &a;
	int **pp = &p;
	int ***p = &pp;
}
int main()
{
	int a = 100;
	int &pa = a;   //对a进行引用
	int &pb = pa;  //对引用再次取引用
	int& &pc = pa  //(不被允许)引用的引用,pa是int&类型
}
指针数组有引用数组无
int main()
{
	int a,b,c;
	char*p[] = {&a,&b&c};  //指针数组
	//证明:数组元素为 int*类型,p是首元素的地址为int**类型(指针的指针有)
	int& p[] = {a,b,c};   //(不被允许)引用数组
	//证明:数组元素为int&类型,p是首元素的地址为int&*类型(引用的指针无)
}
数组的引用
int main()
{
	int arr[5] = {1,2,3,4,5}; 
	//arr的类型为int*const类型,因为指针的指向是不可以发生改变的,但是指针指向的内容可以改变
	int *const &par = arr;   //对数组进行引用 (将数组名当成指针看)
	for(int i=0;i<5;i++)
	cout <<"par[i]= "<< par[i]<<endl;
}
终端输出
par[i] = 1  par[i] = 2  par[i] = 3  par[i] = 4  par[i] = 5
int main()
{
	int arr= {1,2,3,4,5};
	int(& par)[5]  = arr;  //将arr当成数组看
	for(int i=0;i<5;i++)
	cout <<"par[i]= "<< par[i]<<endl;
}
终端输出
par[i]= 1
par[i]= 2
par[i]= 3
par[i]= 4
par[i]= 5

day2

常引用

常引用的特征

const的本意,即不可修改。所以,const对象只能声明为const的引用,使其语义保持一致。non-const 对象,既可以声明const引用,也可以声明为no-const引用。声明const引用,则不可以通过const引用修改数据

int main()
{
//常量
	int &ri = 3;  	    //(不被允许)
    const int &ri = 3;  //(可以) 3为不可变常量
//类型不匹配对象
	double d = 3.14;
	int &rd = d;        //(不被允许)
	const int &rd = d;  //(可以) 此时rd只具有读的属性
}
临时变量的常引用

临时对象:通常不可以取地址的对象,常见的临时值有常量,表达式,函数的返回值,类型不同的变量等

int foo()
{
	int a = 100;
	return a;
}
int main()
{
	const int & cc = 100;   //数字的引用
	int a =3;int b = 5;
	const int & pa = a+b;	//表达式的引用
	const int &pb =  foo(); //函数返回值的引用+
	double d = 10.12;
	const int &fd = d;
} 

new/delete堆内存操作

C语言提供了malloc.free两个系统函数完成对的内存操作。而C++提供两个关键字new和delete,这两个关键字是为类对象而设计的。这两个关键字重点用在类对象的申请与释放,申请的时候会调用构造器完成初始化,释放的时候会调用析构器完成内存的清理

new

语法:类型 + *,若是数组就是数组元素的类型

一维类型

struct Stu
{
	char name[10];
	char sex;
	float score;
};
#include <iostream>
using namespace std;
int main()
{
	int* p = new int;         //通过new + 类型来分配大小为sizeof(类型)的堆内空间
	//int* p =new int(10);    //可以通过()来对值进行初始化初始化的对象是指针指向的内容
	//int **p = new int *; 	  //定义一个指向指针变量的指针变量
	//Stu* ps = new Stu;      //定义一个指向结构体类型的指针
	//float *pp = new float[10]; //定义一个指向一维数组的指针变量
	//char**ppp = new char*[10]; //定义一个指向指针数组的指针变量
	
	cout << *p << endl;
}
终端输出:-842150451      //这说明new分配的空间没有初始化

二维类型

int main()
{
	int(*p)[5] = new int[3][5];  //定义了一个指向二维数组的指针
	//int[3][5]的类型是 int[5] 可以看为type[3]是数组,数组元素类型为int[5]
	for(int i=0;i<3;i++)
	{
		 for(int j=0;j<5;j++)
		 {
		 	p[i][j] = i+j;
		 }
	}
	for(int i=0;i<3;i++)
	{
		 for(int j=0;j<5;j++)
		 {
		 	p[i][j] = i+j;
		 	cout << p[i][j] << " ";
		 }
		 putchar(10);
	}
}
终端输出
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6

三位类型

int(*p)[3][5] = new int [2][3][5];//定义zh'z三位数组的指针
失败

new分配的空间不需要检测是否分配成功即语句 if(xxx == nullptr) exit(-1);cout<<“error”,因为系统会自动检测是否分配空间成功,若不成功则会抛异常,若不处理则会程序终止

用于检测失败的方式
int main()
{
double*pd[50];
try
	{
		for(int i=0;i<50;i++)
		{
			pd[i] = new double[5000000]
		}
	}catch(std::bad_alloc & e)
	 {
	 	cout <<e.what()<<endl;
	 	printf("xxxxxxxxxxxx");
	 }
}	 
一种简单的方式
#include <iostream>
using namespace std;
void newError()
{
	cout << "new error" <<  endl;
	exit(-1);
}
int main()
{
	double* pd[50000];

	set_new_handler(newError);  //检测到喷配内存失败就调用newError
	for (int i = 0; i < 50000; i++)
	{
		pd[i] = new double[99999999];
	}
}

一种熟悉的方式

int main()
{
	double *pd[50];
	for(int i=0;i<50;i++)
	{
		pd[i] = new(nothrow)double[5000000];  //(nothrow)就将new不抛出异常相当于退化为了malloc
		if(pd[i] == nullptr)
		cout<<"new error"<<__FILE__<<__func__<<__LINE__<<endl;
		exit(-1);
	}
}
delete
int main()
{
	int *p = new int;         //摧毁一维的
	delete p;  
    string* pi = new string("china");
	delete pi;
	int *pp =  new int[10] //摧毁多维的
	delete []pp;
	int **ppp = new int *[10]  
	delete []ppp;
	int (*ppp)[5] = new int[3][5];
	delete[]ppp;
}

inline内联函数

引入:

int sqr(int x )
{
	return (x * x); 

}
int main()
{
	int i = 0;
	while (i < 5)
	{
		cout << SQR(i++)<<endl;  //正常的函数调用
	}
	return 0;
}
终端输出
0
1
4
9
16
#define SQR(x) (x * x )
int main()
{
	int i = 0;
	while (i < 5)
	{
		cout << SQR(i++)<<endl;  //调用宏函数,要用替换的思想
	}
	return 0;
}
终端输出
0
4
16

c语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销。但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和可能带来的语意差错,那么是否有一种函数即拥有普通函数的类型检查又可以内嵌,就有了inline 函数。

写法

就是在普通函数前加上inline

inline int sqr(int x )
{
	return (x * x); 

}
int main()
{
	int i = 0;
	while (i < 5)
	{
		cout << SQR(i++)<<endl;  //正常的函数调用
	}
	return 0;
}
终端输出
0
1
4
9
16

作用:inline只是通过避免函数调用这一过程的额外开销,具体再编译时是否发生还要看编译器(建议函数),当此函数短小且频繁使用时,编译器才会采纳,以牺牲代码段的空间而换取时间的效率

Namespace命名空间

命名空间为了大型项目开发,而引入的一种避免命名冲突的一种机制。比如,在一个大型项目中,要用到多家软件开发商提供的类库。在事先没有约定的情况下,两套类库可能存在同名的函数或全局变量而产生冲突。项目越大用到的类库越多,开发人员越多,这种冲突就越明显。

使用(尽量用第一种,少用第二种,不用第三种)
#include<iosteam>
using namespace std;
namespace one       //定义命名空间one
{
	int x = 4;
}
namespace another   //定义命名空间another
{
	int x = 14;
}
int main()  //第一种方式
{
	cout << one::x <<endl;
	cout <<another::x <<endl;
}
int main()  //第二种
{
	using one::a;      //using释放命名空间one中的a,之后不能再释放
	cout << a << endl;
	//using another::a  //(error)不可再次释放
}
int main()             //第三种
{
	using namespace one;  //将命名空间全部释出来,之后不能再次释放
	cout << a << endl;
	//using nanespace another;//(error)
}

string初步

int main()
{
	string s = "china";   //两种初始化方式
	string s1("americal");
	s = "americal"        //可以被赋值
	getline(cin,s);       //向s内输入,可以输空格 etline
	
}
string的一些操作
int main()
{
	string s1 = "great wall";
	string s2 = "China";
	cout << s1.size()<<endl;//求字符串长度
	string s3 = s1 + s2;    //字符串连接
	if(s1 == s2) ...        //字符串比较(< > !=都可以)
	cout << s3 << endl;
	cout<<s1[3]<<endl;     //字符串类型支持下标
	//(字符串对象).c_str用来对接C语言,将字符串对象转为har*类型
	char buf[1024];
	strcpy(buf,s1.c_str);//s
}
字符串与数字互转
#include <string>
int main()
{
	s = "123456";
	int a = stoi(s);    //字符串转int型
	int b = 123456;
	string s1 = to_string(b);   //整型转字符串类型
	cout<<s1<<endl<<a;
}

cast类型转化

新类型的强制转化可以提供更好的控制强转过程,允许控制各种不同种类的强制转换。c++提供了四种转化 static_cast,reinterpret_cast,dynamic_cast,const_cast以满足不同需求,c++风格的强制好处是,他们能更清晰的表明他们要干什么

C++转换风格:xxx_cast (expression)

static_cast

语法格式:static_cast(expresion)

适用场景:再一个方向上可以作隐式转换,再另一个方向上就可以作静态转换

int main()
{
	//双隐
	double d;int i;
	//d = i;i = d;  //两个方向都是被允许的,称为双隐式转换
	d = static_cast<double>(i);
	i = static_cast<double>(d);
	d = static_cast<double>(10/3);
	//单隐
	void *P;int* p;
	//p = q是可以的,q = p不可以,void*类型可以被任意指针类型赋值
	q = static_cast<int *>(p);
	//再比如malloc函数
	q = static_cast<int*>(mallloc(sizeof(10)*10));
}

reinterper_cast 重新解释

语法:reinterpret_cast(expression)

适用场景:通常为操作数的位模式提供较低层的重新解释,也就是将数据以二进制存在形式重新解释,在双方向上都不可以隐式转换的,则需要重新解释类型转换。

int *m;int n;   //这两个类型不能互相转化
//要想互相转化,就要强制
m = reinterpret_cast<int *>(n);
n = reinterpret_cast<int>(m);
const_cast

使用格式:const_cast(expression)

适用场景:用来移除非 const 对象的引用或指针的常量性(const),使用const_cast去除 const 对于引用和指针的限定,通常是为了函数能够接受参数或返回值。注意:目标类型只能是指针或引用,const_cast是对const的一种补充

//const_cast只能作用于指针或引用,去const化
int main()
{
	//对于引用
	int a = 100;
	const int& ra = a;     //此时ra的值不可以发生改变
	const_cast<int&>(ra) = 300;  //对ra进行去const化,ra的值就可以发生改变
	cout << ra << " " << a << " ";
	//对于指针
	const int* p = &a;
	*const_cast<int*>(p) = 100;
	cout<<a;
}
终端输出: 300 300 100

那么如果对象是const类型的呢?

int main()
{
	const int a=100;
	const int&ra = a;
	const_cast<int&>(ra) = 200;
	cout << a << " " << ra << endl;
}
终端输出: 100 200 //这不同于非const对象,但是ra与a的值就不同了,这只改变了ra的值但没有改变a的值
那么再看一个
struct Node
{
	int a;
};
int main()
{
	const Node st = { 40 };
	const Node& pd = st;
	const_cast<Node&>(pd).a = 100;
	cout << pd.a << " " << st.a;
}
终端输出: 100 100  //很奇怪,这怎么又相等了

结论:const_cast只有对非const变量使用const的指针或引用是安全的,若原生就是const的,再对const_cast后的指针或引用执行写操作是未定义的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值