27

経験から学んだ 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 のデータを変更できないわけではありません。

4

16 に答える 16

55

C++ の複雑な関数 typedef 宣言構文を知る必要はありません。ここに私が見つけたかわいいトリックがあります。

クイック、この typedef を説明します。

typedef C &(__cdecl C::* const CB )(const C &) const;

簡単!CB は、C オブジェクトへの const 参照を受け入れ、C オブジェクトへの非 const 参照を返すクラス C のメンバ関数へのポインタです。ああ、それは const メンバー関数です。あ、あと関数ポインタ自体もconst…(ですよね?)

C++ 関数宣言仕様の構文は、あいまいで覚えにくいことで有名です。確かに、経験豊富な C++ のベテランがそのような恐怖を解読するために使用できるトリックはありますが、それはこのヒントの目的ではありません。このヒントは、この恐ろしい構文を覚える必要がなく、そのような関数ポインター typedef を宣言できる方法についてです (たとえば、boost::function を聞いたことがないレガシー API を操作している場合)。精神的な汗をかくのではなく、コンパイラーに作業を任せてください。次回は、次のようなメンバー関数への typedef を作成しようとしています。

struct C {
        const C& Callback(const C&) const   { }
};

上記の複雑な構文を手作業で考え出すのに苦労する代わりに、意図的なコンパイル エラーを引き起こし、コンパイラに野獣の名前を付けさせます。

例えば:

char c = &C::Callback;

コンパイラは、この役立つエラー メッセージを喜んで吐き出します。

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”

それが私たちが探しているものです。:)

于 2008-11-16T16:20:59.983 に答える
18

これは私がいつか捕まえた別のものです:

char int2hex(int x) {
     return "-0123456789abcdef"[(x >= 0 && x < 16) ? (x + 1) : 0];
}

スイッチを実行する代わりに、char配列にインデックスを付けているだけです。範囲外の場合は「-」を返します。

于 2008-11-16T18:53:43.497 に答える
18

いくつかのコードでそれを発見して以来、私はこれが好きです:

assert(condition || !"Something has gone wrong!");

または、手元に条件がない場合は、次のことができます

assert(!"Something has gone wrong!");

以下は@Joshによるものです(コメントを参照)。代わりにコンマ演算子を使用します。

assert(("Something has gone wrong!", condition)); 
于 2008-11-16T15:51:15.737 に答える
14

後で必要になるかどうかわからないときに、クラスにコピー操作を実装しようとして時間を無駄にしないでください。私たちが扱う多くのオブジェクトは単なるエンティティであり、それらをコピーすることはほとんど意味がありません。それらをコピー不可にし、必要が本当に生じた場合は後でコピー/複製を実装します。

于 2008-11-16T16:38:06.517 に答える
11

ヘッダーが汚染され、マクロ名が次のように動作しないことがあります。

#define max(a, b) (a > b ? a : b)

そのように呼び出された max 関数または関数オブジェクトを使用するコードを無効にします。悪名高い例はwindows.h、まさにそれを行うものです。これを回避する 1 つの方法は、呼び出しを括弧で囲むことです。これにより、マクロの使用が停止され、実際の max 関数が使用されます。

void myfunction() {
    ....
    (max)(c, d);
}

現在、最大値は括弧内にあり、マクロへの呼び出しとしてカウントされなくなりました!

于 2009-03-10T11:14:56.820 に答える
7

めったに使用されない便利な C++ イディオムの 1 つは、コンストラクター チェーンでの ?: 演算子の使用です。

class Sample
{  
    const char * ptr;
    const bool  freeable;

    Sample(const char * optional):
        ptr( optional ? optional : new char [32]),
        freeable( optional ? false : true ) {}
    ~Sample( )  { if (freeable) delete[] ptr; }
}  

C++ では、const 値をコンストラクターの本体内で変更することは許可されていないため、これにより const キャストが回避されます。

于 2008-11-16T17:32:27.030 に答える
6

多くの場合、ソース ファイルには、思っているよりもはるかに多くのものを隠すことができます。必要がない場合は、すべてを非公開にしないでください。多くの場合、ソース ファイルの匿名名前空間に残しておく方がよいでしょう。実装の詳細を明らかにする必要はありませんが、モノリシックな関数ではなく、多くの小さな関数を作成するように促されるため、実際には物事の処理が容易になります。

于 2008-11-16T22:13:30.427 に答える
5

私たちは皆、OPを無視して、代わりにお気に入りのクールなトリックを投稿しているので...

ブースト(またはtr1)shared_ptrを使用して、実行時にクラスの不変を維持します(明らかですが、他の人がそれを行うのを見たことがありません):

#include <cassert>
#include <functional>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;

class Foo
{
public:
    Foo() : even(0)
    {
        // Check on start up...
        Invariant();
    }

    void BrokenFunc()
    {
        // ...and on exit from public non-const member functions.
        // Any more is wasteful.
        shared_ptr<Foo> checker(this, mem_fun(&Foo::Invariant));

        even += 1;
        throw runtime_error("didn't expect this!");
        even += 1;
    }

private:
    void Invariant() { assert(even % 2 == 0); }
    int even;
};
于 2008-11-16T20:57:13.093 に答える
5

通常、人々をつまずかせるいくつかのこと:

std::cout << a << a++ << --a;
i = ++i;

上記の行は両方とも未定義です。

void foo(bar* b1, bar* b2);

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

上記はメモリリークの可能性があります。

int* arr = new int[10];
arr + 11;

これにより、未定義の動作が発生します。

イディオムに関しては、私のお気に入りはRAIIです。スタックにオブジェクトを割り当てます。これにより、オブジェクトがスコープ外になったときにデストラクタが呼び出されることが保証され、リソース リークが防止されます。

于 2008-11-16T16:10:34.747 に答える
4

RAII (これまでで最悪の頭字語の 1 つ) とスマート ポインターについて知って以来、メモリとリソースのリークはほぼ完全になくなりました。

于 2008-11-16T21:30:26.833 に答える
3

値のセマンティクスを持たないクラスがある場合は、後で問題が発生しないように、次のすべての構成要素が明示的に宣言されていることを確認してください。

  • デフォルトコンストラクタ
  • コンストラクターのコピー
  • 代入演算子

多くの場合、これらの構造のサブセットを宣言するだけで済みます。ただし、場合によっては、どれが必要でどれが必要でないかについて非常に注意が必要です。3 つすべてを非公開として宣言し、問題を解決する方がはるかに安全です。

また、これがコピー セーフ クラスではないことを説明するコメントを先頭に追加することも非常に役立ちます。

これにより、今後の時間を節約できます。

于 2008-11-16T17:54:52.913 に答える
0

プログラミング中に実際に出くわしたことは何もありませんでしたが、友人がコードが機能する理由についての説明を求めていました。それを理解するのに少し時間がかかりました。皆さんには明らかかもしれませんが、私は経験豊富なプログラマーではありません。

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}
于 2009-03-10T11:34:49.813 に答える
0

私は経験豊富な C++ プログラマーとは言えませんが、関数パラメーターとして配列の配列を渡すことがいかに難しいかを最近学びました。これは絶対に避けてください:(

コンパイル時のサイズがわかっている場合は、簡単です。コンパイル時にディメンションの 1 つがわかっている場合でも。単にわからない場合は...次のようなものを見ている可能性があります

m[i*dim2+j]

i は行の反復子、dim2 は列の数、j は列の反復子です。

于 2008-11-16T21:15:56.673 に答える
0

必要な場合を除き、使用の罠に陥らないでくださいstd::noncopyable。はい、多くの場所で便利であり、そこで使用する必要があります。

トラップはclone()、同じ機能を実装する関数をコピー不可にするとともに関数を書き始めることです。代わりに、explicit( link ) をコピー コンストラクターに使用して、偶発的なコピーを防ぐこともできます (そして、割り当てをプライベートにするか、C++0x で関数を削除します)。clone()ただし、継承された基本クラスには必要です。

于 2013-01-16T11:58:50.897 に答える
0

必要でない限り、使用しないでくださいshared_ptr。代わりにC++ 参照を使用することをお勧めしunique_ptrます。shared_ptrはパフォーマンスを浪費し、コードを実際よりも複雑に見せます。shared_ptrの異常な魅力とその伝染性の性質を除いて、通常、これは問題になりません。

于 2013-01-16T11:50:44.643 に答える
0

パフォーマンス コードの代わりにboost::spirit::hold_any( link ) を使用boost::anyします (多数の小さなオブジェクトを保持している場合)。私は彼らのパフォーマンスに大きな違いを見ました。

于 2013-01-16T12:06:33.730 に答える