C++ 是什么? 您从经验中学到的相关习语、误解和陷阱?
您从经验中学到了哪些与 C++ 相关的习惯用法、误解和陷阱?
一个例子:
class A
{
public:
char s[1024];
char *p;
A::A()
{
p = s;
}
void changeS() const
{
p[0] = 'a';
}
};
即使知道changeS是一个const成员函数,它也在改变对象的值。 因此,const 成员函数仅意味着它将所有变量视为 const,并不意味着它实际上会将所有成员保持为 const。 (为什么?成员函数上的 const 关键字将 char *p; 视为 char * const p; 而不是 const char *p;
这意味着 p 不能指向其他内容。并不是说你不能更改p 的数据。
What are some C++ related idioms, misconceptions, and gotchas that you've learnt from experience?
An example:
class A
{
public:
char s[1024];
char *p;
A::A()
{
p = s;
}
void changeS() const
{
p[0] = 'a';
}
};
Even know changeS is a const member function, it is changing the value of the object. So a const member function only means that it will treat all variables as const, and it does not mean that it will actually keep all members const. (why? the const keyword on the member function treats char *p; as char * const p; And not as const char *p;
Which therefore means that p can't point to something else. And not that you can't change p's data.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
您不需要了解 C++ 复杂的函数 typedef 声明语法。 这是我发现的一个可爱的技巧。
快,描述一下这个 typedef:
简单! CB 是一个指向 C 类成员函数的指针,它接受对 C 对象的 const 引用并返回对 C 对象的非常量引用。 哦,这是一个 const 成员函数。 哦,函数指针本身是 const...(对吗?)
C++ 函数声明规范语法是出了名的迟钝且难以记住。 是的,经验丰富的 C++ 老手可能会使用一些技巧来解读这种恐怖,但这不是本技巧的内容。 这篇技巧是关于如何不需要记住这种可怕的语法,并且仍然能够声明这样的函数指针 typedef(例如,如果您正在与一些从未听说过 boost::function 的遗留 API 进行交互)。
让编译器为您完成工作,而不是费尽心思。 下次当你尝试为成员函数创建一个 typedef 时,如下所示:
与其费力地手动想出上面的复杂语法,不如引入一个故意的编译错误,这将迫使编译器命名这个野兽。
例如:
编译器很高兴地发出这个有用的错误消息:
这就是我们正在寻找的。 :)
You don't need to know C++'s complicated function typedef declaration syntax. Here's a cute trick I found.
Quick, describe this typedef:
Easy! CB is a pointer to a member function of class C accepting a const reference to a C object and returning a non-const reference to a C object. Oh, and it’s a const member function. Oh, and the function pointer itself is const… (Right?)
The C++ function declaration specification syntax is notoriously obtuse and hard to remember. Yes, there are tricks seasoned C++ veterans may use to decipher such horrors, but that’s not what this tip is about. This tip is about how you don’t need to remember this horrible syntax and still be able to declare such function pointer typedefs (e.g. in case you’re interacting with some legacy API that never heard of boost::function).
Instead of breaking a mental sweat, let the compiler do the work for you. Next time you’re trying to create a typedef to a member function that looks like this:
Instead of struggling to manually come up with the complex syntax above, induce an intentional compilation error which will force the compiler to name the beast.
For example:
The compiler happily spews this helpful error message:
Which is what we’re looking for. :)
自从我在某些代码中发现它以来,我就喜欢这个:
或者如果你手头没有条件,你可以这样做
以下内容归因于 @Josh(参见评论)。 它使用 逗号运算符 代替:
I've liked this since the time i've discovered it in some code:
or if you don't have a condition at hand, you can just do
The following is attributed to @Josh (see comments). It uses the comma operator instead:
这是我有一天发现的另一个:
它只是索引 char 数组而不是进行切换。 如果超出范围,则返回“-”。
Here is another one i caught some day:
it's just indexing the char array instead of doing a switch. If it's outside the range, it returns '-'.
当我们不知道以后是否需要时,永远不要浪费时间尝试在类上实现复制操作。 我们处理的许多对象只是实体,复制它们几乎没有任何意义。 使它们不可复制,并在以后确实需要时实施复制/复制。
Never waste time on trying to implement the copy operations on classes when we don't know if it will be required later. Many objects we handle are just entities, and copying them hardly make any sense. Make them non-copyable, and implement the copy/duplication later if the need really arises.
有时,标头会被不具有行为的宏名称所污染,例如
“这将使使用 max 函数或以这种方式调用的函数对象的代码无效”。 一个臭名昭著的例子是
windows.h
,它正是这样做的。 解决这个问题的一种方法是在调用周围加上括号,这会阻止它使用宏并使其使用真正的 max 函数:现在,最大值位于括号中,并且不再算作对宏的调用!
Sometimes, headers are polluted with not behaving macro names like
Which will render code invalid that uses a max function or function object called that way. An infamous example is
windows.h
which does exactly that. One way around it is putting parentheses around the call, which stops it from using the macro and makes it use the real max function:Now, the max is in parentheses and it is not counted as a call to the macro anymore!
一种很少使用但很方便的 C++ 习惯用法是在构造函数链中使用 ?: 运算符。
C++ 不允许在构造函数体内更改 const 值,因此这可以避免 const 强制转换。
One seldom used, but handy C++ idiom is the use of the ?: operator during the constructor chain.
C++ doesn't allow const values to be changed inside the body of the constructor, so this avoids const-casts.
您通常可以在源文件中隐藏比您想象的更多的内容。 如果没有必要,不要将所有内容设为私有 - 通常最好将其保留在源文件中的匿名名称空间中。 我发现,它实际上使事情更容易处理,因为这样你就不会透露实现细节,但会受到启发来制作许多微小的函数而不是单一的函数。
You can often hide way more stuff in source files than you think. Don't make everything private if you don't have to - it's often better to leave it in an anonymous namespace in the source file. It actually makes things easier to process, I find, because then you aren't revealing implementation details, yet get inspired to make lots of tiny functions rather than monolithic ones.
一些通常会让人困惑的事情:
上面的行都是未定义的。
以上可能会泄漏内存。
这会导致未定义的行为。
至于习语,我最喜欢的是RAII。 在堆栈上分配对象,这保证了当对象超出范围时调用析构函数,从而防止资源泄漏。
A few things that usually trip people up:
The above lines are both undefined.
The above may leak memory.
This results in undefined behavior.
As for idioms, my favorite is RAII. Allocate objects on the stack, which guarantees that the destructor is called when the object goes out of scope, preventing resource leaks.
因为我们都忽略了OP,而是发布了我们最喜欢的酷技巧...
使用 boost (或 tr1)shared_ptr 在运行时维护类不变性(有点明显,但我还没有看到其他人这样做):
Since we're all ignoring the OP and instead posting our favourite cool tricks...
Use boost (or tr1) shared_ptr to maintain a class invariant at runtime (kind of obvious, but I haven't seen anyone else do it):
自从我了解 RAII(有史以来最糟糕的缩写之一)和智能指针以来,内存和资源泄漏几乎完全消失了。
Since I learned about RAII (one of the worst acronyms ever) and smart pointer, memory and resource leaks have almost completely disappeared.
如果您有一个没有值语义的类,请确保显式声明以下所有构造,以防止日后出现麻烦。
在许多情况下,您只需要声明这些构造的子集。 然而,在某些情况下,哪些是需要的,哪些是不需要的,这可能会变得非常棘手。 将这三个事件全部宣布为私有并解决这件事会更安全。
在顶部添加注释解释这不是一个复制安全的类也非常有帮助。
这将节省您的时间。
If you have a class which does not have value semantics, make sure that all of the following constructs are explicitly declared in order to prevent headaches down the road.
In many cases you only need to declare a subset of these constructs. However it can get really tricky in some cases as to which are needed and which are not. It's much safer to declare all 3 private and be done with the matter.
It's also very helpful to add a comment to the top explaining that this is not a copy safe class.
This will save you time down the road.
我不能说我是一名经验丰富的 C++ 程序员,但我最近了解到将数组数组作为函数参数传递是多么困难。 尽量避免这种情况:(
如果你知道编译时的大小,那就很简单了。即使你在编译时知道其中一个维度。
如果您根本不知道...您可能会看到类似这样的内容
:i 是行的迭代器,dim2 是列的数量,j 是列的迭代器。
I can't say that I am an experienced C++ programmer but I have recently learned how hard it is to pass and array of arrays as a function parameter. Try to avoid this at all cost :(
If you know the size at compile its simple. Even if you know one of the dimensions at compile time.
If you simply don't know... you might be looking at something like this
Being i the iterator for the rows, dim2 the number of cols and j the iterator for the cols.
我在编程时实际上没有遇到任何问题,但一位朋友想要解释代码为何有效。 我花了一段时间才弄清楚。 也许这对你们来说是显而易见的,但我不是一个经验丰富的程序员。
Nothing I actually came across while programming, but a friend wanted an explanation on why the code worked. It took me a while to figure it out. Maybe it is obvious to you guys, but I am not an experienced programmer.
除非需要,否则不要使用
shared_ptr
。 更喜欢使用 C++ 引用和unique_ptr
。shared_ptr
是一个性能消耗者,使代码看起来比实际更复杂。 通常这不会是一个问题,除了shared_ptr
异常的吸引力及其传染性。Do not prefer to use
shared_ptr
unless needed. Prefer using C++ references andunique_ptr
instead.shared_ptr
is a performance hog and makes the code look more complex than it is. Normally this won't be an issue, except for the unusual attractiveness ofshared_ptr
and its contagious nature.使用
boost::spirit::hold_any
(link) 而不是boost::any
以获得性能代码(同时容纳大量小对象)。 我看到他们的表现有很大的差异。Use
boost::spirit::hold_any
(link) instead ofboost::any
for performance code (while holding a large number of small objects). I saw a large difference in their performances.除非需要,否则不要陷入使用 std::noncopyable 的陷阱。 是的,它在很多地方都很有用,并且应该在那里使用。
陷阱是,人们开始编写
clone()
函数并使其不可复制,这实现了相同的功能。 相反,您也可以使用显式
(链接) 用于复制构造函数,以防止意外复制(并将赋值设为私有,或删除 C++0x 中的函数)。 不过,继承的基类需要clone()
。Do not fall into the trap of using
std::noncopyable
unless needed. Yes, it is useful at a lot of places, and should be used there.The trap is that one starts writing
clone()
function along with making it noncopyable, which implements the same functionality. Instead, you can also useexplicit
(link) for the copy constructor instead to prevent accidental copying (and make assignment private, or delete the function in C++0x).clone()
is needed for inherited base classes though.