您的位置:首页 > 游戏 > 手游 > C++入门Day1

C++入门Day1

2024/10/5 22:28:52 来源:https://blog.csdn.net/U2396573637/article/details/141205856  浏览:    关键词:C++入门Day1

一、C到C++过渡

C++的头文件

C++支持使用C语言的头文件#include <stdio.h>等,也可以使用#include<cstdio>等头文件。这都是C语言的头文件,为了更好的过渡,前期可以这么使用来接受编程语言转型。

那么什么是C++有但C语言没有的呢:#include<iostream>等

这个就是input&output&stream三个单词构成的,顾名思义就是输入输出流。也就是C++的输入输出的操作在里面定义。

C++的输入输出

#include <iostream>
using namespace std;int main(){int a;cin>>a;cout<<a<<": "<<"hello world"<<endl;return 0;
}

cin是输入,cout是输出。>>与<<是两个运算符,流运算符。这也是C++标准输入输出流的一大特点。(>>与<<在C语言中是位运算符,在c++中做了运算符重载的操作,使运算符有了其他的功能)

std::endl是一个函数,流输入输出时,相当于插入一个换行字符刷新缓存区。

1.流插入>>

std::cin是istream类的一个对象,标准输入流

cin>>支持相同数据类型数据连续输入:

int a,b,c;
cin>>a>>b>>c;

cin>>支持不同数据类型数据连续输入:

int a;
char b;
double c;
cin>>a>>b>>c;
2.流提取<< 

std::cout是ostream类的一个对象,标准输出流

cout<< 支持不同类型数据连续输出:(c++自动识别变量类型,本质是根据函数重载实现的)

cout<<输出时,想要控制数据精度需要引用头文件#include <iomanip> 

假设我想将上述的(double)c输出为3位小数,需要这么做:

控制器写法:

cout<<fixed<<setprecision()

方法写法:

cout.set(ios::fixed);

cout.precision();

fixed英文释义为固定,也就是表示后续输出会将精度固定,具体的精度由setprecision()决定,set设置,precision精度,setprecision()设置精度。

 上述相当于:printf("%.3lf\n",c);

如果想控制宽度:

控制器写法:setw()

方法写法:cout.width()

上述相当于:printf("%5d\n",a); 

控制进制:

控制器写法:

cout<<hex

cout<<dec

cout<<oct

方法写法:

cout.setf(ios::hex)

cout.setf(ios::dec)

cout.setf(ios::oct)

上述相当于C语言的占位符,但用法不同,一次控制,后续连续输出均生效。

命名空间

到这里仍然会有一些小疑问,那就是using namespace std到底是个什么东西,写它干嘛。

1.为什么要使用namespace 

在C语言中,变量与函数都是大量存在的,如果给每一个变量和函数都取不同的名字,那..就太难了,不现实啊。那就有人说了,写到不同的文件中就完了呗。对,但是那你要用的时候,需要引用到本文件,假如你要用到两个文件中的各自的某一个函数来完成本文件的功能,但两个文件中都有void Print(),你用的时候,你用的到底是哪个文件里面的呢?这导致的冲突是不可估量的。祖师爷还是祖师爷,设计C++时就为我们想好了解决方法:namespace的概念

namespace相当于不同的文件,但我们可以精准的选用不同区域的某个函数:( :: )域访问符

上述输入输出时,std::endl;endl使用的就是std命名空间里面的函数,而不是其它地方的endl。

使用命名空间的目的是对标识符的名称进行本地化,避免命名冲突或者名字污染。

#include <stdio.h>int printf=0;
int main()
{printf("%d\n",printf);    return 0;
}

这明显是错误的,printf到底是哪的东西,是stdio.h头文件中的函数呢还是自定义的全局变量呢;

另外,转到.c文件中测试一下:(报错:重定义)

在预处理阶段,编译器将printf函数拷贝进本文件,是全局函数,此时全局域还有自定义的int printf,编译器会认为是重定义。而c++有命名空间的概念,只会告诉你不明确,说明只要明确就不会出现重定义的问题:

 

蛙趣,太神奇了吧。怎么样,对命名空间是不是有了一个初步的认识。下面我们就再说说怎么整一个自己的命名空间呢

2.namespace定义与规则

namespace是一个关键字,定义时需要加上这个关键字,就相当于定义一个结构体似的。不信?不信我们看一下:

struct myStr{int a;int b;
};
namespace mySpace{int a;int b;void Print(){cout<<'a'<<endl;}   
}

1.语法:namespace 空间名 {}。(注意没有分号)

空间内部为空间内成员。成员可以是变量、函数、自定义类型等。

也就是说:我可以这么玩


namespace mySpace{struct myStr{int a;int b;};int a;int b;void Print(){cout<<'a'<<endl;}   
}

好家伙,这是神马东西。这个就代表myStr 是mySpace命名空间内的结构体类型,需要使用域访问符来明确是内部的结构体类型。 

访问时,struct类型访问成员是 · 运算符和->运算符访问。namespace则是 :: 上文提到的这个。类比一下,学习会更轻松哦。

2.namespace的本质是定义出一个域,这个域与全局域各自独立,不同的域可以定义同名成员,解决了同名冲突问题

相信大家都玩过游戏、看过小说,在起名环节是不是都有过名字被占用的问题。游戏就是全局域,你不能有相同的name,但是假设我有这么一个规则:相同的服务器不能有同名玩家,但不同的服务器可以有。那么服务器就是从全局域这个大世界独立出来的一个小世界。如果大世界需要召唤小世界的玩家,那么玩家名字前就需要加上前缀:01号服务器-Stark,02号服务器-Stark。这么说,能明白了吗。

3.命名空间可以嵌套
namespace Students{namespace zhangsan{string name;int age;void eat(){cout<<"吃"<<endl;}void drink(){cout<<"喝"<<endl;}void play(){cout<<"玩"<<endl;}void info(){cout<<"从小到大的经历"<<endl;}}namespace lisi{string name;int age;void study(){cout<<"学习"<<endl;}void info(){cout<<"从小到大的经历"<<endl;}}
}

学生这个大的命名空间内,有张三的空间,有李四的空间。 

4.命名空间可以合并

假如有两个命名空间,名字相同。其实这就是一个命名空间,这就是所谓的合并。

namespace A{int ma;int mb;
}
namespace A{void fun(){}
}//等价于
namespace A{int ma;int mb;void fun(){}
}
5.命名空间的使用

C++的标准库都放在std(standard)这个命名空间中。

第一种:指定访问

#include <iostream>int main(){std::cout<<"123"<<std::endl;return 0;
}

第二种:展开成员

#include <iostream>
using std::endl;int main(){std::cout<<"123"<<endl;return 0;
}

第三种: 展开全部

#include <iostream>
using namespace std;int main(){cout<<"123"<<endl;return 0;
}

bool类型 

int a=true;
int b=false;
bool bl=0;
bool al=5;cout<<a<<" "<<b<<" "<<al<<" "<<bl<<" "<<endl;

 输出结果为:1 0 1 0 明明给al赋值为5,为什么输出的却是1呢?为什么赋值true,a却输出1?对于一系列疑问,在这里,我们可以看一下C++中的bool类型数据的相关规则:

bool类型:
    只占一个字节,只有 0 和 1
    0代表false   1代表true
    作用:用来做标记
    任何非零值都是ture,存在bool变量中输出均为1
    

C与C++的区别:
    C语言中,没有bool类型

nullptr关键字 

在C++中使用NULL给指针变量赋值是不安全的,C++专门引入了一个关键字叫做:nullptr空指针。

NULL在C语言中是一个宏,在stddef.h中被定义为:

#ifndef NULL#ifdef _cplusplus#define NULL 0#else #define NULL ((void*)0)#endif
#endif

也就是说:

C++中NULL被定义为字面量0,不建议赋给指针变量

 C语言中NULL被定义为五类型指针void*的常量


  二、初识:new运算符

new的用法

申请连续空间和一块空间时的用法。

int* fun1(){int* n = new int[3];//开辟数组,大小为3,[]//释放时需要delete[]return n;
}
int* fun2(){int* n = new int(1);//开辟元素,数值为1,()//释放时需要deletereturn n;
}

对于申请连续空间的数组时,想要对其进行初始化,操作方法:

int* fun(){int *n=new int[3]{1,2,3};//1 2 3int *m=new int[3]{0};//全为0return n;
}

内存泄漏

int* fun()
{int* n=new int[3];return n;
}
int main()
{//情况一:fun();//调用函数申请堆区空间,申请的空间没有释放//情况二:int *p=fun();//接收地址delete p;//释放地址,但申请时申请的是连续的多块地址,此处只释放数组首元素地址//正确写法:int *ptr=fun();delete[] ptr;return 0;
}

C与C++的申请堆区空间的区别: 

new: 在堆区申请内存空间
        如果申请的是数组返回的就是数组首元素地址
        如果申请的是一个元素,返回的就是元素的内存地址

new 和 malloc 的区别:
        1.malloc 需要返回强转返回值,new不需要
        2.malloc 需要传入具体的字节数,new不需要
        3.malloc 申请内存失败时会返回空指针,new申请内存失败时会抛出异常
        4.new是运算符,malloc是函数
        //5.new = malloc() + operator new + 异常处理

内存泄露:

        申请的空间没有手动释放


 三、初识:引用类型

引用类型:
     引用:给变量起别名
     语法:数据类型 &别名=原名
引用特点:
    必须初始化,且不能初始化为空
    引用不能改变引用关系
    //一旦初始化,不可再将这个别名赋给别的变量使用
引用的底层:
    DataType* const p;
    //指针指向不可改变 即 引用关系不可改
    //const修饰必须初始化 即 引用必须初始化

 引用作返回值

int& test01() {int a = 10;return a;
}
int& test02() {static int a = 10;return a;
}
int main() {int _b1=test01();for(int i=0;i<5;i++) cout<<_b1<<" ";cout<<endl;//将a的值返回赋给_b1,输出5个10int& b1=test01();for(int i=0;i<5;i++) cout<<b1<<" ";cout<<endl;//输出1~2个10,其余随机值//由于a被释放掉,返回a的引用,a不存在,所以是随机值//准确值是编译器保留的,使用过后就没有了,这个是伪·正确值int _b2=test02();for(int i=0;i<5;i++) cout<<_b2<<" ";cout<<endl;//输出5个10int& b2=test02();for(int i=0;i<5;i++) cout<<b2<<" ";cout<<endl;//由于a被static修饰,a不会被释放掉,b2就是a的别名,所以输出5个10return 0;
}

内存分区:C语言的内存知识_c语言内存-CSDN博客

静态变量:static修饰的变量

static修饰全局变量,该全局变量不能被extern声明

static修饰局部变量,该局部变量在定义的函数内一直存在,不被释放

 const的用法

int main() {int a = 3;const int* p1 = &a;//使用p1代表a,则a 只读不可写,p1不改指向int* const p2 = &a;//使用p2代表a,则a 可读可写,p2课改变指向const int* const p3 = &a;//使用p3代表a, 则a 只读不可写,p3不改指向return 0;
}
const int p=2;
int* p1=&p;//error
int* const p1=&p;//error
const int*p1=&p;//正确
//指针指向一个变量的时候,const属性不能删除
//假如变量为const修饰,指针指向该变量,我可以通过指针修改该变量的值,const修饰失效,不合理int n=2;
const int* p1=&n;
//假设变量未被修饰,指针指向该变量时可以增加const属性,意义是使用指针时对变量进行只读操作//也就是说指针指向变量时,cosnt属性不可删除,cosnt属性可添加

 左值与右值

//左值引用:DataType& _a;
//右值引用:DataType& &a;
//万能引用:const DataType & aa;
int test01() {int a = 10;return a;//返回:数值,右值
}
int& test02() {static int a = 10;return a;//返回:引用,左值
}
int main() {int _a = 2;//_a是左值int& a = _a;//(左值引用)引用左值int&& b = 2;//(右值引用)引用右值//int&& b2 = _a;//(右值引用)不能引用左值//int& a2 = 2;//(左值引用)不能引用右值const int& aa = test01();//(万能引用)引用右值const int& bb = test02();//(万能引用)引用左值return 0;
}

四、函数重载 

函数参数默认值

在C++中,函数的参数可以设置默认值。例如:

void function(int a=1,int b=2,int c=3){cout<< a <<" "<< b <<" "<< c <<endl;
}

让我们在主函数中测试一下,函数参数默认值怎么使用:

int main()
{fun();//1 2 3fun(11);//11 2 3fun(11,22);//11 22 3fun(11,22,33);//11 22 33return 0;
}

  显而易见,当没有传入实参时,形参会使用默认值进行初始化。但当传入参数时,该参数相当于重新赋值,初始化的默认值被新值覆盖,也就不会使用默认值。但不能说我想让a和c使用我的值,b使用默认值,这是不可以的。C++中,函数参数默认值必须是参数列表从右向左依次赋予默认值,不可跳过任何一个去间隔设置默认值。

函数参数默认值:
    1.如果当前位置有参数默认值,那么后面的参数必须也有参数默认值
    (参数列表从右向左依次赋予缺省值/默认值)

    2.在函数声明时有参数默认值,函数定义时不能有参数默认值

对于第二点的情况,请看代码:

void func(int a = 10);
int main() {func();return 0;
}
void func(int a) {//上面声明时已经设置默认值,这里不要写默认值了//a默认为10cout << a << endl;
}

 如果不相信,你可以试一下在声明和定义时都赋予默认值。结果是:

函数重载

函数重载:函数名相同的情况下,函数体可以不同。

重载的前提:

1.函数名相同

2.参数列表不同(参数个数不同,参数类型不同,参数类型相同但顺序不同)

注意:

函数的重载与函数返回值类型无关

函数重载时,确保不会因为函数参数默认值而发生二义性

1.参数个数不同
void fun(){//零个参数cout<<"0"<<endl;
}
void fun(int){//一个参数cout<<"1"<<endl;
}
void fun(int,int){//两个参数cout<<"2"<<endl;
}
int main(){fun();//0fun(0);//1fun(0,0);//2
}
2.参数种类不同 
void fun(int){cout<<"int"<<endl;
}
void fun(float){cout<<"float"<<endl;
}
void fun(double){cout<<"double"<<endl;
}
void fun(char){cout<<"char"<<endl;
}
int main(){fun(1);//intfun(1.1f);//floatfun(1.1);//doublefun('1');//charreturn 0;
}
3.参数顺序不同 
void fun(int,char){cout<<"int char"<<endl;
}
void fun(char,int){cout<<"char int"<<endl;
}
int main(){fun(1,'1');//int charfun('1',1);//char intreturn 0;
}
 4.默认值与重载

上面我们就提到了,函数参数有默认值时,可以缺省一些实参的传入。那么就有一个问题:

void fun(int a){cout<<a<<endl;
}
void fun(int a,int b=10){cout<<a<<b<<endl;
}
int main()
{fun(3);//这里调用的是哪个funreturn 0;
}

主函数调用的到底是哪一个fun函数呢?好像调用哪一个都行啊,编译器是不会识别你到底想调用哪个的,那么此时就存在二义性。编译器会报错:

函数重载的原理 

在C语言的学习中我们为什么没有听过函数重载的概念呢,到了C++函数为什么就能重载了呢?相信你会有这个疑问。

先简单的直接说结论:函数名修饰规则所致

C语言编译时,只将函数名作为标志,如果有另一个函数同名,无论参数一不一样都会报重定义的错误

C++编译时,将函数名与参数列表一同作为标志,具体规则我们后文学习linux时展开。


感谢大家!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com