//hello world程序 #includeusing namespace std; int main() { cout << "hello world" << endl; //相当于C的printf return 0; }
C++对类型的检测更为严格 如:malloc的使用
再比如const
#include真正的枚举using namespace std; int main() { const int a = 100; int *q = &a; //这是不能通过编译,因为 &a的类型为 const int *,而 q 是int*类型不同C++报错 }
#include字符串类型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
函数同名不同参也就是重载
#includeusing namespace std; void func(int a) { printf("%dn", 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
运算符的重载也是函数的重载 <<
#includeusing 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; }
//构造类型是不能比较的所以使用操作符的重载来达到目的 #includeusing 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; }
函数重载的规制:
- 函数名相同。
- 参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
- 返回值类型不同则不可以构成重载。(无法通过编译) //anbiguous (二义性)
#include关于进制的输出using namespace; int main() { int a,b,c; cin>>a>>b>>c; //往a,b,c内输入数据 //cin表示流输入运算 cout<<"a = "<
#include域宽,对齐,填充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#include //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 < 默认参数 规则:
- 默认的顺序,是从右向左,不能跳跃。
- 函数声明和定义一体时,默认认参数在定义(声明)处。声明在前,定义在后,默认参 数在声明处。
- 默认值可以是常量,全局变量,或是一个函数
- 冲突 一个函数,不能既作重载,又作默认参数的函数。当你少写一个参数时,系统无法 确认是重载还是默认参数。(当实现功能,既能用默认参数又可以使用重载时,推荐使用默认参数)
#includeReference &引用 定义:using namespace std; void func(int a){} void func(int a,int b=2){} int main() { func(1,5) //很明显是在调用第二个函数 func(4) //这就说不清楚了,可以理解为调用第一个函数,也可以理解为调用第二个函数,第二个参数的默认值为2 } 变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个 别名。
- 引用是一种声明关系,不开辟空间
- 引用必须要初始化,不能单独存在
- 与被别名的变量拥有相同的数据类型
- 声明关系一旦确立不可变更
- 可以对引用再次引用,也就是对一个变量可以进行建立多个引用,此时引用之间是平行关系
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引用的提高 指针的引用(int *&)有,引用的指针(int& *)无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 #includeusing 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]<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]<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; }; #includeusing 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 <一种简单的方式 #includeusing 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__<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++)<#define SQR(x) (x * x ) int main() { int i = 0; while (i < 5) { cout << SQR(i++)<c语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销。但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和可能带来的语意差错,那么是否有一种函数即拥有普通函数的类型检查又可以内嵌,就有了inline 函数。
写法就是在普通函数前加上inline
inline int sqr(int x ) { return (x * x); } int main() { int i = 0; while (i < 5) { cout << SQR(i++)<作用:inline只是通过避免函数调用这一过程的额外开销,具体再编译时是否发生还要看编译器(建议函数),当此函数短小且频繁使用时,编译器才会采纳,以牺牲代码段的空间而换取时间的效率
Namespace命名空间命名空间为了大型项目开发,而引入的一种避免命名冲突的一种机制。比如,在一个大型项目中,要用到多家软件开发商提供的类库。在事先没有约定的情况下,两套类库可能存在同名的函数或全局变量而产生冲突。项目越大用到的类库越多,开发人员越多,这种冲突就越明显。
使用(尽量用第一种,少用第二种,不用第三种)#includeusing namespace std; namespace one //定义命名空间one { int x = 4; } namespace another //定义命名空间another { int x = 14; } int main() //第一种方式 { cout << one::x < 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()<!=都可以) cout << s3 << endl; cout< 字符串与数字互转 #includeint main() { s = "123456"; int a = stoi(s); //字符串转int型 int b = 123456; string s1 = to_string(b); //整型转字符串类型 cout< 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_castreinterper_cast 重新解释(i); i = static_cast (d); d = static_cast (10/3); //单隐 void *P;int* p; //p = q是可以的,q = p不可以,void*类型可以被任意指针类型赋值 q = static_cast (p); //再比如malloc函数 q = static_cast (mallloc(sizeof(10)*10)); } 语法:reinterpret_cast(expression)
适用场景:通常为操作数的位模式提供较低层的重新解释,也就是将数据以二进制存在形式重新解释,在双方向上都不可以隐式转换的,则需要重新解释类型转换。
int *m;int n; //这两个类型不能互相转化 //要想互相转化,就要强制 m = reinterpret_castconst_cast(n); n = reinterpret_cast (m); 使用格式: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(ra) = 300; //对ra进行去const化,ra的值就可以发生改变 cout << ra << " " << a << " "; //对于指针 const int* p = &a; *const_cast (p) = 100; cout< 那么如果对象是const类型的呢?
int main() { const int a=100; const int&ra = a; const_cast(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 (pd).a = 100; cout << pd.a << " " << st.a; } 终端输出: 100 100 //很奇怪,这怎么又相等了 结论:const_cast只有对非const变量使用const的指针或引用是安全的,若原生就是const的,再对const_cast后的指针或引用执行写操作是未定义的



