C++入门篇1

一、C++关键字

与c语言的32个关键字不同,C++一共有63个关键字,这里不做详细介绍,等后面用到会一一介绍

 二、命名空间

命名空间的出现主要是防止出现命名冲突的情况,在C语言中,我们不能同名定义,尤其是我们定义的变量名,函数名等不能和库里面的命名相同,为了解决这种情况,C++有了namespace这个关键字,用法如下

#include<iostream>
//命名空间里可以定义变量,结构体,函数
//跟结构体类似
//相当于在全局里面又开辟了一块独立的空间
namespace zxws{//这里的zxws就是这个命名空间的名字,自己随便取
    int rand;//这个变量名其实和库里的rand()函数重名,但在命名空间里,可以很好的区分开来

    int Add(int x,int y){
        return x+y;
    }

    struct ListNode{
        int val;
        struct ListNode*next;
    };
}

那么我们如何用命名空间里的变量/函数/结构体呢?共有三种用法如下

方法一:命名空间的名字+作用域限定符::

namespace zxws {
    int rand;

    int Add(int x, int y) {
        return x + y;
    }

    struct ListNode {
        int val;
        struct ListNode* next;
    };
}
int Add(int x, int y) {
    return x + y;
}
//方法一:命名空间的名字+作用域限定符::
int main() {
    printf("%p\n", rand);//这个是库里的函数的地址
    printf("%d\n", zxws::rand);//这个是命名空间里的变量
    printf("%d\n", Add(1, 2));//这个是在命名空间外的加法函数
    printf("%d\n", zxws::Add(1, 2));//这个是在命名空间里的加法函数
    struct zxws::ListNode s;
    return 0;
}

方法二:使用using将命名空间里的某个成员引入

#include<iostream>
namespace zxws {
    int rand;

    int Add(int x, int y) {
        return x + y;
    }

    struct ListNode {
        int val;
        struct ListNode* next;
    };
}
//int Add(int x, int y) {
//    return x + y;
//}
using zxws::Add;//这里的意思就是将Add这个函数放开,即可以在全局访问这个函数
//但是如果这时外面已经有了Add函数,就会出问题,编辑器会不知道访问哪个函数,这就是为什么将Add函数注释掉的原因
//using zxws::rand;//问题同上
using zxws::ListNode;
int main() {
    printf("%d",Add(1,2));
    struct ListNode s;
    return 0;
}

方法三:using+命名空间

namespace zxws{
    int rand;
    int Add(int x, int y) {
        return x + y;
    }

    struct ListNode {
        int val;
        struct ListNode* next;
    };
}
using namespace zxws;//将命名空间里定义的所有东西放开
int main(){
    //printf("%d",rand);//这时编译器就会分不清要打印哪个rand
    printf("%d",Add(1,2));
    return 0;
}

这里有两点补充:

1.一个工程中允许有多个相同名称的命名空间,编辑器会将它们合并成一个

2.命名空间是可以"无限套娃"的,如下

//套娃
namespace zxws{
    int a=0;
    //...
    namespace zxw{
        int b=1;
        //...
        namespace zx{
            //...
        };
    };
}

//访问
using zxws::zxw::b;
//using namespace zxws;//注意使用这种方法不能访问b,因为b在zwx里面,得用下面的那行代码
using namespace zxws::zxw;
int main(){
    printf("%d",b);
    printf("%d",zxws::zxw::b);
    return 0;
}

三、C++的输入、输出

#include<iostream>
using namespace std;
//打开标准库的命名空间,因为cin/cout/endl在命名空间里面
int main(){
    int a=10;
    //因为C++兼容C,所以scanf/printf函数依旧可以用,注意要包含头文件
    //scanf("%d",&a);
    //printf("%d",a);
    cin>>a;//输入---自动识别类型
    cout<<a<<endl;//输出---自动识别类型,endl等价于'\n'
    return 0;
}

四、缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
举个例子: 

#include<iostream>
using namespace std;
void Print(int a = 0) {
    cout << a << endl;
}
int main(){
    Print();//不传参时,a默认为0
    Print(10);//传参时,a就等于传过来的值
    return 0;
}

 

 缺省参数又分为两种:全缺省和半缺省

//全缺省
void Print1(int a=1,int b=2,int c=3){
    cout<<a<<" "<<b<<" "<<c<<endl;
}
//半缺省(部分缺省)
void Print2(int a,int b,int c=10){
    cout<<a<<" "<<b<<" "<<c<<endl;
}

int main(){
    //传参时,从左到右依次对应
    Print1();
    Print1(10);
    Print1(10,20);
    Print1(10,20,30);
    cout<<endl;

    Print2(10,20);
    Print2(10,20,30);
    return 0;
}

 注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现,声明给,定义不给
  3. 缺省值必须是常量或者全局变量

五、函数重载

重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数完成不同的功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。不能只有函数返回值类型不同

//1.参数个数不同
void f(){
    cout<<"void f()"<<endl;
}

void f(int x){
    cout<<"void f(int x)"<<endl;
}

//2.参数的类型不同
void f(char x){
    cout<<"void f(char x)"<<endl;
}

void f(double x){
    cout<<"void f(double x)"<<endl;
}

//3.参数的顺序不同
void f(int x,float y){
    cout<<"void f(int x,float y)"<<endl;
}

void f(float x,int y){
    cout<<"void f(float x,int y)"<<endl;
}

使用场景:

//交换
void Swap(int*p1,int*p2){
    int tmp=*p1;
    *p1=*p2;
    *p2=tmp;
}

void Swap(double*p1,double*p2){
    double tmp=*p1;
    *p1=*p2;
    *p2=tmp;
}

int main(){
    int a = 10, b = 20;
    cout << a << " " << b << endl;
    Swap(&a, &b);
    cout << a << " " << b << endl;

    double x = 1.1, y = 2.2;
    cout << x << " " << y << endl;
    Swap(&x, &y);
    cout << x << " " << y << endl;
    return 0;
}

 六、引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。从语法上来说,引用不占用空间,但是,从引用的底层实现来说,它其实使用指针实现的,所以会占用空间

int main() {
    int a = 0;
    //指针
    int* p = &a;
    (*p)++;
    //引用
    int& c = a;
    c++;
    return 0;
}

从上面的汇编代码我们可以看出指针和引用的实现是一样的,所以引用在做参数时的效率就会比传值要高。

那么,问个问题:下面代码的运行结果一样吗?

int main() {
    int a = 10;
    int& ra = a;
    printf("%p\n", &a);
    printf("%p\n", &ra);
    return 0;
}

相信很多人在看完上面说的引用和指针的实现一样就会认为,ra和a的地址不同,但结果真的如此吗?

答案显而易见,虽然实现的方式一样,但是语法层面是不一样的,指针是存放地址的变量,引用就是取了某个变量的别名,那么既然是别名,那么语法本质上来说,ra和a是通一个东西,是同一块空间的不同名称,就和人有小名也有大名,我们不能因为他的叫法不同,而认为他不是同一个人

指针和引用之间的联系和区别:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
int main() {
    int a = 10;
    //int& x;//会报错
    int& b = a;
    int& c = a;
    //...
    return 0;
}

当然,引用也有用const修饰的常引用,如下

int main() {
    int a = 10;
    //权限的平移,对a的操作,对b也能执行
    int& b = a;
    //权限的缩小,常引用不能修改值
    const int& c = a;

    const int x = 10;
    //权限的放大,不被允许
    //int& y = x;//会报错
    return 0;
}

使用场景:

//做参数
void swap(int& x, int& y) {
    int tmp = x;
    x = y; 
    y = tmp;
}

//做返回值
int& f() {
    static int c = 5;
//注意这里的c一定要是全局/静态的,这样出了作用域才不会被销毁,不然就会出现类似野指针的情况
    return c;
}

七、内联函数

内联函数其实是为了替代宏而产生的,它在宏的基础上更上一层楼,属于取其精华去其糟泊了。

首先,我们用宏写的一个加法来复习一下宏是什么

#define ADD(x,y) ((x)+(y))

那么上面的代码有什么缺点,优点(宏的优点缺点)

优点:1.加强了代码的复用性    2.提高了性能(不需要建立栈帧)

缺点:1.不方便调试 (宏替换是预编译期间做的)  2.代码的可读性差    3.没有严格的类型检查

由此,C++创建了inline这个关键字,用来改善宏的缺点

inline int ADD(int x, int y) {
    return x + y;
}

int main() {
    cout << ADD(1, 2) << endl;
    return 0;
}

上面写的ADD函数就是一个内联函数,写法上和普通函数没啥区别,单纯加了一个关键字inline,但是编辑器在处理时,就会将ADD在调用的地方之间展开,不会和函数一样去建立栈帧,提高了效率。

但需要我们注意的是,内联函数在编译期间不会生成地址(因为是在调用位置直接展开,所以不需要地址),所以内联函数的声明和定义要写在一起,不然编辑器会找不到内联函数,其次,内联函数不是我们说了算的,我们只是建议将某个函数设为内联函数,具体还得看编辑器,就和register关键字一样(一般只有小规模的函数才能被设置成内联函数)

八、auto关键字

随着程序越来越复杂,就会出现很复杂的类型,这时候就需要用到我们的auto关键字,可以自动的推导类型,用法如下:

#include <string>
#include <map>
int main() {
    //一般情况下,没啥用
    int a = 1;
    auto b = a;
    //但是当类型复杂之后,就会很有用(我们现在只要知道下面是一种类型)
    std::map<std::string, std::string> m;
    //这时我们就需要用到auto
    auto x = m;
    //这里介绍一个函数用来看函数的类型
    cout << typeid(m).name() << endl;
    cout << typeid(x).name() << endl;
    return 0;
}

打印的是最原本的类型名,上面代码中的类型名还是被 typedef 过的。

注意:

  • auto在定义变量时一定要初始化,编辑器要根据你给的数据来进行推导类型,
  • 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

auto不能推导的场景:

1.不能作为函数参数

2.不能定义数组

3.如下

int main() {
    auto x = 1, y = 1.0;//报错,编辑器只会对第一个变量进行推导,后面的变量需要保证类型一致
    return 0;
}

C++11标准还引入了一种新的auto用法,如下:

int main() {
    //这里就先然大家认识一下这种写法,后面会和大家具体讲这种语法规则
    int arr[] = { 1,2,3,4,5,6,7,8,9 };
    for (auto& x : arr) {
        x *= 2;
    }
    for (auto x : arr) {
        cout << x << " ";
    }
    return 0;
}

 九、空指针

在C++中,NULL被定义为0,nullptr才是空指针,nullptr作为关键字存在,不要包头文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值