PoEdu培训 C++班 第一课 C过度到C++(1)
文章类别: 培训笔记 0 评论

PoEdu培训 C++班 第一课 C过度到C++(1)

文章类别: 培训笔记 0 评论

C过度到C++

为什么要学C++

C++是由C语言升级而来, 它集成了C的效率和灵活.
C++是一系列"语言"的"结合体", 拥有多种编程范式和各种编程语言优点.

编程范式

C++支持很多编程范式, 比如函数式, 基于对象, 面向对象, 模板元编程, 泛型等等.

C++相对于C的新增特性

C++中增加了一些新的特性, 我们来看一看.

bool

C语言中的bool我们在使用的时候, 需要使用头文件 stdbool.h
而我们的C++中,这是原生支持的数据类型.

原生bool和C里边的bool的区别

在原生bool类型中, 0 表示 false, !0 表示 true, 它是原生的.
而在C中的bool, 0 表示 false, 1 表示 true, 它是使用#define定义的.

以 `-1` 这个值来举例

在原生bool中, -1为true, 是定义的行为.
而在非原生bool中, -1为未定义的行为.
虽然最终的结果可能是正确的, 但是, 这是因为
编译器在进行布尔运算的时候, 替我们进行了大量操作.

头文件

我们的C++是由C进化发展而来
它具有几个标准
我们使用 .h 头文件, 说明它是由C继承来的, 符合C标准

#include <stdio.h>
而使用 cXXX, 说明它是由C集成来的, 并且符合C++标准
#include <cstdio>

而使用我们的无后缀名的头文件, 说明是我们的C++标准库

#include <iostream>

预编译头

"预编译头"是VC独有的, 是属于微软提供的"便利".
它不被C++标准所承认.

VS中可以设置是否使用"预编译头".

新的 printf

在C++中, 我们使用 std::cout 来代替 printf 函数.

需要加上  #include <iostream> 头文件哦~
// 举个例子
#include <iostream>

int main()
{
    int num = 20;
    std::cout << num;
    std::cout << "Hello CPP!";
    return 0;
}

域运算符(Domain Operator)

我们来看一下这个例子
// 域运算符例子
#include <iostream>

// 定义全局变量
int num = 100;

int main()
{
    // 局部变量
    int num = 20;
    std::cout << "局部变量:";
    std::cout << num;
    std::cout << "全局变量:"
    std::cout << ::num;
    return 0;
}

:: 就是 域运算符, 它可以访问指定命名空间的变量.
当我们的 域运算符 为空的时候, 代表的是 全局域
当我们的 域运算符 前有指定的时候, 这个指定就是命名空间

比如我们例子中的 std::, std 就是一个命名空间.

命名空间

命名空间 可以使用 namespace 来进行声明.

举个例子
// 域运算符例子
#include <iostream>

using namespace std;

// 定义全局变量
int num = 100;

namespace Hades
{
    int num = 50;
}

int main()
{
    // 局部变量
    int num = 20;
    cout << "局部变量:";
    cout << num;
    cout << "全局变量:"
    cout << ::num;
    cout << "Hades命名空间变量:"
    cout << Hades::num;
    return 0;
}

使用 using 关键字可以指定使用的命名空间.

比如 using namespace std; 表示默认使用 std 命名空间.
那么, 在使用 std::cout 的时候, 就不需要在加 命名空间 的名字了.

多个命名空间重名的情况这里暂不讨论.

new 和 delete

空间进行操作.
对应C语言中的函数为 malloc 和 free

// 例子
#include <iostream>

using namespace std;

int main()
{
    // 声明一个int的指针, 并将指针所指向的值设置为100
    int *pNum = new int(100);
    cout << *pNum;
    // 千万不要忘了delete
    delete pNum;

    // 在堆上进行分配int数组, 长度为10
    int *pArray = new int[10];
    // 注意, 上面那句话, 数组数据并未初始化.

    // 删除的时候, 需要使用数组方式删除
    delete[] pArray;

    return 0;
}

new 和 delete是运算符, 而malloc 和 free是函数!

重载 overload

重载 是C++中是一个非常强大的 机制.

// 重载例子
#include <cstdio>

void HadesCout(int num)
{
    printf("%d", num);
}

void HadesCout(char* szStr)
{
    printf("%s", szStr);
}

void HadesCout(char c)
{
    printf("%c", c);
}

void HadesCout(double dNum)
{
    printf("%f", dNum);
}

int main()
{
    HadesCout("Hello C++\n");
    HadesCout(100);
    HadesCout(10.0);
    HadesCout('H');
    return 0;
}

函数名相同, 但是它的参数表的个数或顺序,类型不同, 构成重载!
返回值不同不能构成重载.
函数名相同, 参数表个数,类型,顺序都相同, 参数名称不同, 会造成函数重定义.

// 注意
//    参数类型是值的参数的类型, 而不是参数的名称.
//    比如:
int func(int a);
void func(int b);
// 这两个是不构成重载的, 它会造成函数重定义!
// ==================================
// 参数个数相同, 类型顺序不同, 也会重载.
// 比如
int func(int a, char c);
int func(char c, int a);
// 这两个是构成重载的!

此处场景前提: 无Class, 无多个命名空间, 只有1个CPP文件.

重载的原理(仅在易于理解方面)

重载函数的生成, 实际上是分成了几个不同的函数.
虽然函数名相同, 但是函数地址不同.
这个工作是由编译器帮我们做的.
每个函数空间都是独立的.
编译器记录了一个符号表.
包括了函数的参数, 变量, 类型等信息.
在调用的时候进行匹配, 然后再去调用.

命名粉碎(命名重定向)

由于重载机制, 在我们编译的过程中, 函数名称会发生改变.
在我们项目内部调用的时候, 因为我们的编译器有记录符号表, 所以调用正常.
但是在我们提供给外部进行调用的时候(比如编译成dll), 我们的函数名就会发生变化, 从而导致调用失败.

这里不进行详细研究, 我们只做一个了解.

最常用的解决办法, 是使用 extern "C" { /* 各种语句 */ } 的方式进行编译.

未完待续

如有错误,请提出指正!谢谢.

回复