在我看来,C ++的危险被夸大了。
根本的危险是:尽管C#允许您使用unsafe
关键字执行“不安全”的指针操作,但C ++(主要是C的超集)将使您可以在需要时使用指针。除了使用指针(与C相同)固有的常见危险(例如内存泄漏,缓冲区溢出,指针悬空等)外,C ++还为您提供了新的方法来严重破坏问题。
可以这么说,乔尔·斯波斯基(Joel Spolsky)所说的 “额外的绳索” 基本上可以归结为一件事:编写内部管理自己的内存的类,也称为“ 3规则 ”(现在可以称为“规则”) C ++ 11中的4或5的规则)。这意味着,如果您想编写一个内部管理自己的内存分配的类,则必须知道您在做什么,否则程序可能会崩溃。您必须仔细创建一个构造函数,复制构造函数,析构函数和赋值运算符,这很容易出错,通常会在运行时导致异常崩溃。
但是,在实际的日常C ++编程中,编写一个管理自己的内存的类确实非常罕见,因此说C ++程序员总是需要“小心”以避免这些陷阱是一种误导。通常,您只会做更多类似的事情:
class Foo
{
public:
Foo(const std::string& s)
: m_first_name(s)
{ }
private:
std::string m_first_name;
};
该类看起来非常类似于您在Java或C#中所做的事情-它不需要显式的内存管理(因为该库类std::string
会自动处理所有事务),并且自默认以来根本不需要“ 3规则”的东西复制构造函数和赋值运算符就可以了。
只有当您尝试执行以下操作时:
class Foo
{
public:
Foo(const char* s)
{
std::size_t len = std::strlen(s);
m_name = new char[len + 1];
std::strcpy(m_name, s);
}
Foo(const Foo& f); // must implement proper copy constructor
Foo& operator = (const Foo& f); // must implement proper assignment operator
~Foo(); // must free resource in destructor
private:
char* m_name;
};
在这种情况下,对于新手来说,正确地分配分配,析构函数和复制构造函数可能很棘手。但是在大多数情况下,没有理由这样做。C ++通过使用像std::string
和这样的库类,很容易避免99%的时间进行手动内存管理std::vector
。
另一个相关的问题是以不考虑引发异常的可能性的方式手动管理内存。喜欢:
char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;
如果some_function_which_may_throw()
确实确实引发异常,s
则将导致内存泄漏,因为分配给它的内存将永远无法回收。但是,实际上,由于“ 3规则”不再是一个太大的问题,在实践中这不再是一个问题。用原始指针实际管理自己的内存非常罕见(通常是不必要的)。为避免上述问题,您所需要做的就是使用std::string
或std::vector
,并且在抛出异常之后在堆栈展开期间会自动调用析构函数。
因此,这里的总主题是许多不是从C继承的C ++功能,例如自动初始化/销毁,复制构造函数和异常,迫使程序员在C ++中进行手动内存管理时要格外小心。但是同样,这只是一个问题,如果您打算首先进行手动内存管理,那么在拥有标准容器和智能指针的情况下,几乎不再需要手动进行内存管理。
因此,我认为,尽管C ++为您提供了很多额外的好处,但几乎没有必要使用它来吊死自己,而Joel所谈论的陷阱在现代C ++中很容易避免。
Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.
。我相信这符合这样一个问题……