c

如果您已经了解 C,如何学习 C++

从 C 迁移到 C++ 容易吗?

是的!C++ 几乎完全是标准 C95(C90 和 1995 年修正案 1)的超集。除了少数例外,每个有效的 C95 程序也是一个有效的 C++ 程序,且含义相同。

一个很好的第一步是简单地将 C++ 用作“一个更好的 C”,这意味着你可以在 C++ 的 C 子集中进行编程,并发现体验比 C 更好,因为 C++ 提供了额外的类型检查,有时甚至对普通的 C 代码也提供额外的性能。

当然,C++ 还提供了更多!一旦你开始将现有的 C 代码编译为 C++,你就可以根据自己的舒适程度,选择性地、策略性地在各个地方使用 C++ 功能——并立即在每一行代码中看到好处。

C++ 和 C 有什么区别?

C++ 是 C95(C90 加一个修正案)的直接后代,它保留了几乎所有 C95 作为子集。C++ 提供了比 C 更强的类型检查,并直接支持比 C 更广泛的编程风格。C++ 是“一个更好的 C”,因为它支持使用 C 进行编程的风格,具有更好的类型检查和更多的符号支持(不损失效率)。同样地,ANSI C90/C95 是比 K&R C 更好的 C。此外,C++ 支持数据抽象、面向对象编程和泛型编程(参见《C++ 程序设计语言》;讨论兼容性问题的附录 B 可供下载)。

我们从未见过可以用 C95 比 C++ 更好地表达的程序(我们不认为这样的程序会存在——C95 中的每个构造在 C++ 中都有一个明显的等价物)。然而,仍然存在一些环境,其中对 C++ 的支持非常薄弱,以至于使用 C 更有优势。不过,这样的环境已经不多了;请参见Stroustrup 的(不完整)编译器列表

有关 C++ 设计的讨论,包括其与 C 的关系,请参见《C++ 的设计和演变》

请注意,上面段落中的“C”指的是经典 C 和 C95(带有修正案的 C90)。C++ 不是 C99 的后代;相反,两者都源自 C95。C++11 采用了所有 C99 的预处理器扩展和库扩展,但没有采用 C99 的新语言特性,因此像 C99 中添加的 restrict 关键字这样的语言特性通常不是 ISO C++ 的一部分。这里是C++98 和 C99 之间差异的描述。

C 是 C++ 的子集吗?

在严格的数学意义上,C 不是 C++ 的子集。有些程序在 C 中是有效的,但在 C++ 中不是;甚至有一些编写代码的方式在 C 和 C++ 中具有不同的含义。然而,C++ 支持 C95(C90 加一个修正案)及更早版本支持的所有编程技术。每个这样的 C 程序都可以在 C++ 中以基本相同的方式编写,具有相同的运行时和空间效率。将数万行 ANSI C 转换为 C 风格的 C++ 通常只需几个小时。因此,C++ 是 C95 的超集,就像 C95 是 K&R C 的超集,就像 ISO C++ 是 1985 年存在的 C++ 的超集一样。

编写良好的 C 代码通常在 C++ 中也是合法的。例如,Kernighan & Ritchie:《C 程序设计语言(第二版)》中的每个示例也都是一个 C++ 程序。

C/C++ 兼容性问题示例

int main()
{
    double sq2 = sqrt(2);   /* Not C++: call undeclared function */
    int s = sizeof('a');    /* silent difference: 1 in C++ sizeof(int) in C */
}

调用未声明的函数在 C 中是不良风格,在 C++ 中是非法的。向函数传递参数时使用未列出参数类型的声明也是如此。

void f();   /* argument types not mentioned */

void g()
{
    f(2);   /* poor style C. Not C++ */
}

在 C 中,void* 可以隐式转换为任何指针类型,并且自由存储分配通常使用 malloc() 完成,后者无法检查是否请求了“足够”的内存。

void* malloc(size_t);

void f(int n)
{
    int* p = malloc(n*sizeof(char));  /* not C++. In C++, allocate using `new' */
    char c;
    void* pv = &c;
    int* pi = pv;   /* implicit conversion of void* to int*. Not in C++ */
}

请注意 void* 隐式转换为 int* 导致的潜在对齐错误。参见void*malloc() 的 C++ 替代方案

从 C 转换为 C++ 时,请注意 C++ 比 C 拥有更多的关键字。

int class = 2;    /* ok in C. Syntax error in C++ */
int virtual = 3;  /* ok in C. Syntax error in C++ */

除了一些上面所示的(并在 C++ 标准和《C++ 程序设计语言(第三版)》附录 B 中详细列出的)示例外,C++ 是 C 的超集。(附录 B 可供下载)。

请注意,上面段落中的“C”指的是经典 C 和 C95(带有修正案的 C90)。C++ 不是 C99 的后代;相反,两者都源自 C95。C++11 采用了所有 C99 的预处理器扩展和库扩展,但没有采用 C99 的新语言特性,因此像 C99 中添加的 restrict 关键字这样的语言特性通常不是 ISO C++ 的一部分。这里是C++98 和 C99 之间差异的描述。

既然有老式可靠的 qsort(),为什么还要使用 sort()

对初学者来说,

    qsort(array,asize,sizeof(elem),elem_compare);

看起来很奇怪,比

    sort(vec.begin(),vec.end());

更难理解。对于专家来说,sort() 对于相同的元素和相同的比较标准往往比 qsort() 更快,这一事实通常很重要。此外,sort() 是泛型的,因此可以用于容器类型、元素类型和比较标准的任何合理组合。例如

    struct Record {
        string name;
        // ...
    };

    struct name_compare {   // compare Records using "name" as the key
        bool operator()(const Record& a, const Record& b) const
            { return a.name<b.name; }
    };

    void f(vector<Record>& vs)
    {
        sort(vs.begin(), vs.end(), name_compare());
        // ...
    }   

如果你的编译器支持 C++14,这会变得更简单

    struct Record {
        string name;
        // ...
    };

    void f(vector<Record>& vs)
    {
        sort(vs.begin(), vs.end(), [](auto &a, auto &b) { return a.name < b.name; });
        // ...
    }   

此外,大多数人赞赏 sort() 是类型安全的,使用它不需要强制转换,并且他们不必为标准类型编写 compare() 函数。

有关更详细的解释,请参阅 Stroustrup 的论文“将 C++ 作为一种新语言学习”,可从他的出版物列表下载。

sort() 倾向于优于 qsort() 的主要原因是比较更好地内联。

为什么我必须使用强制转换才能从 void* 进行转换?

在 C 中,你可以隐式地将 void* 转换为 T*。这是不安全的。考虑

    #include<stdio.h>

    int main()
    {
        char i = 0;
        char j = 0;
        char* p = &i;
        void* q = p;
        int* pp = q;    /* unsafe, legal C, not C++ */

        printf("%d %d\n",i,j);
        *pp = -1;   /* overwrite memory starting at &i */
        printf("%d %d\n",i,j);
    }

使用没有指向 TT* 可能会产生灾难性的后果。因此,在 C++ 中,要从 void* 获取 T*,你需要进行显式强制转换。例如,要获得上面程序的 undesirable 效果,你必须编写

        int* pp = (int*)q;

或者,使用新风格的强制转换使未检查的类型转换操作更明显

        int* pp = static_cast<int*>(q);

最好避免强制转换。

这种不安全转换在 C 中最常见的用途之一是将 malloc() 的结果分配给合适的指针。例如

    int* p = malloc(sizeof(int));

在 C++ 中,使用类型安全的 new 运算符

    int* p = new int;

顺便说一句,new 运算符比 malloc() 具有额外的优势

  • new 不会意外地分配错误的内存量,
  • new 隐式检查内存耗尽,并且
  • new 提供初始化功能

例如

    typedef std::complex<double> cmplx;

    /* C style: */
    cmplx* p = (cmplx*)malloc(sizeof(int)); /* error: wrong size */
                            /* forgot to test for p==0 */
    if (*p == 7) { /* ... */ }          /* oops: forgot to initialize *p */

    // C++ style:
    cmplx* q = new cmplx(1,2); // will throw bad_alloc if memory is exhausted
    if (*q == 7) { /* ... */ }