C ++中经常被误解的概念是什么?

时间:2009-02-18 12:26:32

标签: c++

c ++中经常被误解的概念是什么?

38 个答案:

答案 0 :(得分:80)

C ++不是带有类的C!

并没有一种叫做C / C ++的语言。一切都从那里开始走下坡路。

答案 1 :(得分:40)

C ++ 具有自动资源管理功能。

(大多数声称C ++没有内存管理的人试图使用new和delete方法太多,没有意识到如果他们允许C ++自己管理资源,那么任务就会变得容易了。)

示例:(由于我没有时间检查文档,因此使用组合的API制作)

// C++
void DoSomething()
{
  File file("/tmp/dosomething", "rb");
  ... do stuff with file...
  // file is automatically free'ed and closed.
}

// C#
public void DoSomething()
{
  File file = new File("/tmp/dosomething", "rb");
  ... do stuff with file...

  // file is NOT automatically closed.
  // What if the caller calls DoSomething() in a tight loop?
  // C# requires you to be aware of the implementation of the File class
  // and forces you to accommodate, thus voiding implementation-hiding
  // principles.
  // Approaches may include:
  // 1) Utilizing the IDisposable pattern.
  // 2) Utilizing try-finally guards, which quickly gets messy.
  // 3) The nagging doubt that you've forgotten something /somewhere/ in your
  //    1 million loc project.
  // 4) The realization that point #3 can not be fixed by fixing the File
  //    class.
}

答案 2 :(得分:35)

自由函数不仅仅因为它们不在类中而 C ++不仅仅是一种OOP语言,而是建立在一堆技术之上。

当人们说自由函数(名称空间和全局命名空间中的函数)是“C次的残骸”时,我听过很多次,应该避免。恰恰相反。自由函数允许从特定类中分离函数并允许重用功能。如果函数不需要访问实现细节,也建议使用自由函数而不是成员函数 - 因为当更改类的实现以及其他优点时,这将消除级联更改。

这也反映在语言中:C++0x(即将发布的下一个C ++版本)中基于范围的for循环将基于自由函数调用。它将通过调用自由函数beginend来获得开始/结束迭代器。

答案 3 :(得分:27)

分配和初始化之间的区别:

string s = "foo";    // initialisation
s = "bar";           // assignment

初始化始终使用构造函数,赋值始终使用operator =

答案 4 :(得分:22)

按降序排列:

  1. 确保释放已分配内存的指针
  2. 当析构函数应该是虚拟的
  3. 虚拟功能如何运作
  4. 有趣的是,没有多少人知道虚拟功能的全部细节,但似乎仍然可以完成工作。

答案 5 :(得分:21)

我见过的最有害的概念是它应该被视为带有一些插件的C.实际上,对于现代C ++系统,它应该被视为一种不同的语言,而且我看到的大部分C ++ - 抨击都是基于“C with add-ons”模型。

提一些问题:

虽然你可能需要知道deletedelete[]之间的区别,但通常都不应该写。使用智能指针和std::vector<>

事实上,你应该很少使用*。对字符串使用std :: string。 (是的,设计很糟糕。无论如何都要使用它。)

RAII意味着您通常不必编写清理代码。清理代码是糟糕的风格,并破坏概念性的地方。作为奖励,使用RAII(包括智能指针)可以免费为您提供许多基本的异常安全性。总的来说,它在某些方面比垃圾收集要好得多。

一般而言,不应通过public或使用getter和setter直接显示类数据成员。有一些例外(例如点类中的x和y),但它们是例外,应该被视为例外。

最重要的一点:没有C / C ++这样的语言。可以编写可以在任何一种语言下正确编译的程序,但是这些程序不是很好的C ++,并且通常不是很好的C.自Stroustrup开始研究“C with Classes”以来,这些语言已经发生了分歧,现在不像永远。使用“C / C ++”作为语言名称是用户不知道他或她在说什么的表面证据。适当使用的C ++不再像C或Java那样C。

答案 6 :(得分:19)

过度使用与多态无关的继承。大多数情况下,除非你真的使用运行时多态,组合或静态多态(即模板)更好。

答案 7 :(得分:13)

静态关键字,可能意味着三种不同的事物之一,具体取决于使用位置。

  1. 它可以是静态成员函数或成员变量。
  2. 它可以是在命名空间范围内声明的静态变量或函数。
  3. 它可以是在函数内声明的静态变量。

答案 8 :(得分:13)

数组不是指针

他们是不同的。所以&array不是指向指针的指针,而是指向数组的指针。在我看来,这是C和C ++中最容易被误解的概念。你必须访问所有那些告诉我们将2-d数组传递为type**的答案!

答案 9 :(得分:12)

以下是一些:

  1. 使用模板实现不带vtable的多态,àlaATL。
  2. 逻辑const - ness vs actual const - 内存中的内容。何时使用mutable关键字。

  3. 致谢:谢谢你纠正我的错误,spoulson。

    编辑:

    以下是更多:

    1. 虚拟继承(不是虚拟方法):事实上,我根本不理解它! (那个,我的意思是我不知道它是如何实现的)
    2. 其成员为对象的联合,其各自的类具有非平凡的构造函数。

答案 10 :(得分:12)

这是C ++中一个经常被遗忘的重要概念:

  

C ++不应该像对象一样简单地使用   面向Java或C#等语言。   从STL中激发灵感并编写通用代码。

答案 11 :(得分:12)

鉴于此:

int x = sizeof(char);

X是什么价值?

您经常听到的答案取决于对规范的理解程度。

  1. 初学者 - x是1,因为字符总是8位值。
  2. 中级 - 它取决于编译器的实现,字符可以是UTF16格式。
  3. 专家 - x是一个并且始终是一个,因为char是内存的最小可寻址单位,而sizeof确定存储该类型实例所需的内存单元数。因此,在char为8位的系统中,32位值的sizeof为4;但在char为16位的系统中,32位值的sizeof为2。
  4. 不幸的是,标准使用'byte'来指代一个内存单元,因为许多程序员认为'byte'是8位。

答案 12 :(得分:10)

来自c:

的c ++初学者的经典之作

混淆deletedelete[]

编辑:

使用C API时所有经验水平中的另一个经典失败:

std::string helloString = "hello world";
printf("%s\n", helloString);

而不是:

printf("%s\n", helloString.c_str());

每周都会发生在我身上。您可以使用流,但有时您必须处理类似printf的API。

答案 13 :(得分:10)

C ++是一种多范式语言。许多人严格地将C ++与OOP联系起来。

答案 14 :(得分:9)

有些事情似乎让人们经常混淆或不知道:

  1. 指针,特别是函数指针和多指针(例如int(*)(void *),void ***)

  2. const关键字和const正确性(例如const char *,char * const和const char * const之间的区别是什么,以及void class :: member()const是什么意思?)

  3. 内存分配(例如,每个新指针都应删除,malloc / free不应与new / delete混合,何时使用delete []而不是delete,为什么C函数仍然有用(例如expand(),realloc()))

  4. 范围(即您可以单独使用{}为变量名称创建新范围,而不是仅仅作为if的一部分,等等...)

  5. 切换语句。 (例如,不理解他们可以优化(或在某些情况下更好)优于ifs链,不理解通过及其实际应用(循环展开作为示例)或存在默认情况)

    < / LI>
  6. 调用约定(例如cdecl和stdcall之间的区别是什么,如何实现pascal函数,为什么它甚至重要?)

  7. 继承和多重继承,更一般地说,是整个OO范例。

  8. 内联汇编程序,通常是实现的,不是C ++的一部分。

答案 15 :(得分:9)

指针。

取消引用指针。通过.->

在需要指针时使用&的地址。

通过在签名中指定&来引用参数的函数。

指向指针***的指针或按引用void someFunc(int *& arg)的指针指针

答案 16 :(得分:8)

  • 指向成员的指针和指向成员函数的指针。
  • 非类型模板参数。
  • 多重继承,尤其是虚拟基类和共享基础对象。
  • 构造和破坏的顺序,构建中间基类的虚函数的状态。
  • 铸造安全和可变尺寸。不,您不能在可移植代码中假设sizeof(void *) == sizeof(int)(或任何其他类型,除非便携式标头明确保证)。
  • 指针算法。

答案 17 :(得分:7)

如果函数接受指向指针的指针,void*仍将执行此操作

我已经看到 void指针的概念经常被混淆。相信如果你有一个指针,你使用void*,如果你有一个指针指针,你使用void**。但是你可以而且应该在两种情况下使用void*void**没有void*具有的特殊属性。

这是一个特殊属性,void*也可以被赋予指向指针的指针,当被强制转换时,会收到原始值。

答案 18 :(得分:7)

标题和实施文件

这也是许多人误解的概念。如果函数定义在一侧的程序中多次出现而在类别定义在另一侧出现多次时,则会出现如何进入头文件及其导致链接错误的问题。

非常类似于这些问题,为什么标题保护非常重要。

答案 19 :(得分:5)

我认为关于C ++最容易被误解的概念是它存在的原因以及它的目的是什么。它经常受到上面的攻击(Java,C#等)和下面的(C)。 C ++能够在机器附近操作以处理计算复杂性和抽象机制来管理域复杂性。

答案 20 :(得分:5)

NULL始终为零。

许多人将NULL与地址混淆,并认为如果平台具有不同的空指针地址,则不一定为零。

NULL始终为零,而且不是地址。它是一个零常量整数表达式,可以转换为指针类型。

答案 21 :(得分:4)

呵呵,这是一个愚蠢的回答:C ++编程中最容易被误解的是模板类无法编译时来自g ++的错误消息!

答案 22 :(得分:4)

std::vector在使用保留时不会创建元素

我已经看到程序员认为,如果他们size()加注到那个位置,他们可以访问位置大于reserve()的成员。这是一个错误的假设,但在程序员中非常普遍 - 特别是因为编译器很难诊断错误,这会使得“无效”。

答案 23 :(得分:4)

内存对齐。

答案 24 :(得分:3)

C ++不是典型的面向对象语言。

不相信我?看看STL,模板比对象更多。

使用Java / C#编写面向对象代码的方式几乎是不可能的;它根本不起作用。

  • 在Java / C#编程中,有很多new,许多实用程序对象实现了一些单一的内聚功能。
  • 在C ++中,必须删除任何对象new ed,但始终存在谁拥有该对象的问题
  • 因此,往往会在堆栈上创建对象
  • 但是当你这样做的时候,如果你要将它们传递给其他函数/对象,你必须一直复制它们,因此浪费了许多据说可以通过非托管环境实现的性能。 C ++
  • 在意识到这一点后,您必须考虑其他组织代码的方法
  • 你可能最终以程序的方式做事,或使用像智能指针这样的元编程习语
  • 此时,您已经意识到C ++中的OO不能像在Java / C#中那样使用#

Q.E.D.

如果你坚持用指针做oop,你通常会有大型(巨大的!)类,在对象之间有明确定义的所有权关系,以避免内存泄漏。然后,即使你这样做,你已经离oop的Java / C#成语太远了。

  

实际上我编写了“面向对象”一词,我可以告诉你我没有考虑到C ++。
   - Alan Kay(点击链接,这是视频,引用时间是10:33)

虽然从纯粹主义的角度来看(例如Alan Kay),即使Java和C#也没有真正的oop

答案 25 :(得分:3)

C结构VS C ++结构经常被误解。

答案 26 :(得分:3)

指针是迭代器,但迭代器并不总是指针

这也是一个经常被误解的概念。指向对象的指针是随机访问迭代器:它可以由任意数量的元素递增/递减,并且可以读取和写入。但是,具有运算符重载的迭代器类也可以满足这些要求。所以它也是一个迭代器,但当然不是指针。

我记得我过去的一位C ++老师正在教导(错误地),如果你做vec.begin(),你会得到一个指向矢量元素的指针。他实际上假设 - 不知道 - 向量使用指针实现其迭代器。

答案 27 :(得分:3)

C ++不是带字符串和向量的C!

答案 28 :(得分:1)

  1. 对象,堆栈或堆的分配。
  2. 使用const。
  3. 朋友的使用

答案 29 :(得分:1)

  • 当人们在C ++中创建静态变量时,匿名命名空间几乎总是真正需要的。
  • 制作库头文件时,pimpl习惯用法(http://www.gotw.ca/gotw/024.htm)应该用于几乎所有私有函数和成员以帮助进行依赖管理

答案 30 :(得分:1)

我仍然不明白为什么vector没有pop_front以及我无法排序的事实(list.begin(),list.end())..

答案 31 :(得分:1)

我知道这是一个古老的问题,但我认为对象切片/​​堆栈中的对象的多态性失败值得一提。如果我六个月没有使用C ++,当我再次使用该语言时,这个就会出现并咬我。

#include <iostream>

class base {
    public:
    base(int val) { var1 = val; };
    virtual void doSomething() { var1 *= 2; };
    int getVar1() { return var1; };
    virtual ~base() { };
    protected:
    int var1;
};

class deriv : public base {
    public:
    deriv(int val) : base(val) { };
    void doSomething() { var1 *= 4; };
};

void use_object_ptr(base *arg) {
    arg->doSomething();
    std::cout << arg->getVar1() << std::endl;
};

void use_object_copy(base arg) {
    arg.doSomething();
    std::cout << arg.getVar1() << std::endl;
};

int main(int argc, char **argv) {
    deriv d(42);

    deriv *p2d = new deriv(42);

    use_object_ptr(p2d); // calls deriv::doSomething(), prints 168

    use_object_copy(d); // calls base::doSomething(), prints 84

    use_object_ptr(&d); // calls deriv::doSomething(), prints 168

    return 0;
}

答案 32 :(得分:0)

C ++或C / C ++?我想说最容易被误解的部分是内存管理和指针。前者是因为人们没有适当的自由(太早或根本没有!)而后者因为很多人只是没有“得到”指针(无论是通过引用传递“伪装”还是通常类似于链表)。

答案 33 :(得分:0)

对于某些人来说,继承和多态之间的区别可能是一个令人困惑的概念。

答案 34 :(得分:0)

为什么A [b]与b [A]相同?

好吧,这不是一个普通的问题,但它出现在我曾经教过的课上......

答案 35 :(得分:0)

  1. 指针(*)和参考(&amp;)
  2. 之间的区别
  3. 使用指针访问多维数组。
  4. 使用*来访问指针的值 - 即* ptr = 5
  5. 何时释放已分配的内存。

答案 36 :(得分:-2)

非常好的资源我不能厌倦推广 - C++ Frequently Questioned Answers

答案 37 :(得分:-3)

一个重要的是语言不是100%语法兼容。 C ++与C完全链接兼容,但是某些C ++样式语法会在C中生成编译器错误。某些编译器不挑剔,不遵循C标准。当从一种语言转移到另一种语言时,需要学习这些基础知识。注意我还没有阅读最新的C标准,但最后我知道这是真的。