C是C++的子集,在C语言的基础上添加了面向对象的特性,以及C++标准库。

1. C++关键字

 C++总计63个关键字,C语言32个关键字

2. 命名空间

2.1 命名空间的语法

 定义命名空间,我们用到一个新的关键字namespace,这个关键字后是命名空间的名字,然后用一个{},{}内的是命名空间的成员。
 

// 关键字 名字
namespace u1
{
    int a; // 一个普通的int型变量
    // 也可以是一个函数
    int add(int a,int b)
    {
        return a + b;
    }
}
namespace u2
{
    // 命名空间里面也可以嵌套命名空间
    namespace u3
    {
        int d1;
        int d2;
    }
}
// 如果说同一个工程下的不同文件,都有相同的命名空间,编译器最后会将其合并

// test1.cpp
namespace u4
{
    int add(int a,int b)
        return a + b; 
}

// test2.cpp
namespace u4
{
    int mul(int a,int b)
        return a * b;
}

// 最后这两个函数都是在u4这个命名空间下,不会冲突

注:命名空间其实相当于一个作用域,命名空间{}内的内容都是在这个空间里面,我们c++的标准库都在std这个命名空间下,如果想使用c++标准库的函数,容器,需要在前面加上用std这个命名空间

 2.2 命名空间的用法

命名空间的使用方法有三种:

(1)

// 1.命名空间名称及作用域限定符
int main()
{
    printf("%d\n",u1::a);
    return 0;
}

(2) 

// 使用using将命名空间中成员引入
using namespace u1::a; // 意思就是讲u1下的a变量展开,这时候它的作用域就是全局作用域

int main()
{
    printf("%d\n",a); // 这时候不用加命名空间和作用域限定符
    return 0;
}

(3)

using namespace u1; // 意思是将u1整个命名空间展开

int main()
{
    printf("%d\n",a);
    return 0;
}

3. C++的输入和输出

这是一个最简单的C++程序,里面包含了一个头文件iostream,一个main函数以及一句打印函数,我们看到在cout这个函数前面,加上了一个std::,这是因为iostream库是属于标准库的一块,在std这个命名空间下。

标准输入输出对象 

标准库定义了4个IO对象,IO(输入输出),以下:

  1. cin是一个istream流对象,现在理解为标准输入即可。
  2. cout是一个ostream流对象,理解为标准输出即可。
  3. cerr是用来输出警告和错误信息的,所以也成为标准错误
  4. clog是用来输出程序运行的一般信息。

 一个C++程序

(1)输出

// 编写一个简单的c++程序

#include <iostream> // C++标准库的iostream库,包含了c++的输入和输出函数

int main()
{
    // 打印hello world
    std::cout << "hello world" << std::endl;
}

(2)输入

#include <iostream>

int main()
{
    int a,b;
    std::cin >> a >> b; // 输入a和b
    std::cout << a << " " << b << std::endl;
    return 0;
}

 注:如果不想每次在cin或者cout前加上std::,可以使用前面讲的命名空间使用方法,很多人会使用using namespace std;,但是建议不要去这样做,因为c++标准库的内容都在std这个命名空间下,如果使用这句语句,会将标准库的内容暴露出来,那么像cout或者cin这些即常用又不像使用std::,可以使用以下方法。

using namespace std::cout;
using namespace std::cin;

 4. 缺省参数

4.1 缺省参数的定义和使用

 缺省参数是声明或者定义一个函数,为参数指定一个默认值

#include <iostream>
using namespace std::cout;
using namespace std::cin;

int fun(int a = 0)
{
    return a;
}

int main()
{
    cout << fun();   // 0
    cout << fun(1);  // 1 
    return 0;
}

带默认值的参数,如果在传参时,没有传入指定参数,则该函数会使用这个缺省参数。 

4.2 缺省参数的分类 

  •  全缺省参数 
  •  半缺省参数
// ... 头文件

// 全缺省参数
int add(int a = 0, int b = 1)
{
    return a + b;
}

int main()
{
    cout << add() << endl;     // 结果为1
    cout << add(1, 2) << endl;  // 结果为3
    cout << add(2) << endl;    // 结果为3
    //add(, 1); 这个是错的,不能跳过第一个参数,传给第二个
    return 0;
}
// ... 头文件

// 半缺省参数
int add(int a, int b = 1)
{
    return a + b;
}

int main()
{
    cout << add() << endl;     // 错误的,因为a没有默认参数
    cout << add(1, 2) << endl;  // 结果为3
    cout << add(2) << endl;    // 结果为3
    return 0;
}

总结

  1. 面对带默认参数的函数,在全缺省的情况下,可以不传入参数就进行调用,如果只传入一部分参数,那么这些参数默认从前面开始一一传入。但是要注意一点,不可以跳过前面的参数进行传入。
  2. 在半缺省的情况下,半缺省并不是指缺省一半的意思,是指有至少一个缺省,却不是全缺省。半缺省指定默认参数的时候,如果一个变量设置了默认参数,那么它后面的参数都要设置默认参数。

5. 函数重载 

C++允许函数在统一作用域下出现同名函数,这些同名参数的参数个数或类型或顺序可以不同。

int add(int a,int b)
{
    return a + b;
}

int add(double a,double b)
{
    return a + b;
}

int add(int a,int b,int c)
{
    return a + b + c;
}

// 这里会发生函数重载失败
double add(double a,double b)
{
    return a + b;
}

注:只有参数个数和类型还有顺序不同才可以发生重载,如果只是返回值不一样,编译器会认为是同一个函数。

6. 引用

6.1 引用概念

 引用的含义是给一个变量取一个别名,它是和引用的变量共享同一块空间。

int main()
{
    // 类型& 引用变量名 = 引用对象
    int a = 10;
    int& b = a; // b是a的别名
    cout << a << " " << b;
    return 0;
}

结果都是10,但要注意一点,引用对象和别名的类型都要属于同一种类型。

6.2 引用特性

  1. 引用必须在开始初始化
  2. 一个变量可以有多个引用
  3. 引用一旦初始化后,就不可以再引用其他变量
int main()
{
    int a = 10;
    // int& ra; // 这条会报错
    int& ra = a;
    int& rra = a;
    int b = 20;
    ra = b; // 这句话不是修改引用对象,而是赋值
    return 0;
}

 6.3 常引用

int main()
{
    const int a = 10;
    // int& ra = a; // 这里会报错
    const int&ra = a; 

    int b = 10;
    const int& rb = b; // 这里没问题
}

结论:

  1. 无法使用非const的引用对常量进行引用
  2. 可以对变量进行const引用,那么这个const引用的属性为只读 

6.4 使用场景

 在c语言中,如果想写一个交换函数,我们需要通过指针传地址,将其修改,在c++中,我们可以使用引用,引用本质上其实也是传地址的一种方式,但是引用更具有可读性。

int swap(int &a,int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

 下面这段代码会输出什么,如果不加static会怎么样

int& Count()
{
    static int n = 0;
    n++;
    // ...
    return n;
}

我们来看下非static的情况,这时的n是一个局部变量,我们知道局部变量出了函数作用域就会销毁,那这时候将一个局部变量进行引用返回,这个内存空间其实已经还给系统了。虽然第一次能够成功打印出结果,但是第二次是一个随机值。这不是我们想要的。 

int& Count()
{
    int n = 0;
    n++;
    // ...
    return n;
}

但如果是加上static的话,会是这个局部变量变为全局变量,全局变量的作用域是一直到整个程序结束。 

6.5 传值和传址效率对比

C语言中,我们在进行传参的时候,会考虑使用两种传参方式,一种是传值,一种是传地址,其实c++的引用本质上就是传地址的方式。参考下面的代码,我们在传递一个数组的时候,如果采用传值的方式,会创建一个临时的拷贝,效率是非常低的,尤其是传入的参数特别大的时候。

#include <time.h>

struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}

void TestRefAndValue()
{
    A a;
    // 以值作为函数参数
    size_t begin1 = clock();
    for (size_t i = 0; i < 10000; ++i)
        TestFunc1(a);
    size_t end1 = clock();
    // 以引用作为函数参数
    size_t begin2 = clock();
    for (size_t i = 0; i < 10000; ++i)
        TestFunc2(a);
    size_t end2 = clock();
    // 分别计算两个函数运行结束后的时间
    cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
    cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

建议:尽可能使用const引用,我们前面知道进行引用传递的效率比传值效率高,但是,在有些场景下,我们并不想修改传入参数的值,这个时候可以使用const引用传址。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐