2016-12-23 14 views
1
class Word 
{ 
private: 
    std::string w; 
public: 
    Word(std::string w) : w(w) {} 
}; 

class Sentence 
{ 
private: 
    std::list<Word *> words; 

public:   
    Sentence(std::list<Word *> words) : words(words) {} 

    ~Sentence() { 
     for (auto word : words) 
      delete word; 
     words.clear(); 
    } 
}; 

int main() { 
    Word *word1 = new Word("abc"); 
    Word *word2 = new Word("def"); 
    Sentence sentence1(std::list<Word *>({word1, word2})); 
    Sentence sentence2 = sentence1; 
    return 0; 
} 

(実際のクラスが大きくなっている。私は本当に、他の機能に割り当てられ、Wordへのポインタを使用する必要があります) オブジェクトword1word2は二回削除されます。メモリ管理の正しい方法

  1. 方法Word * Word::clone(const Word *word)Sentence * Sentence::clone(const Sentence *s)を追加し、Sencence::operator=にそれらを呼び出す:私はこの問題を解決するため、これらのオプションを持っています。しかし、この場合私のプログラムはあまりにも多くのメモリを使います(文中の単語は同じですが、メモリに2回割り当てられます)。
  2. スマートポインタを使用します。私のプログラムは少し効率が悪くなり(コードはより複雑になります)。
  3. bool isCopySentenceを追加し、isCopy == falseの場合にのみ単語を削除してください。それは馬鹿に見える、と思う。

どうすればよいですか?

+0

あなたは何が必要ですか?概念的には同じ文章の異なるコピーが含まれているかどうか?前者の場合は、ポインタをまったく使用せず、すべてを値で渡します。後者の場合は、文章をより長くし、unique_ptrに格納し、参照または非所有の生ポインタで渡すだけで、範囲内の単語を割り当てることを強くお勧めします。しかし、私はあなたがどんな種類の行動をしたいのか分からないので、私はその質問に答えることができません。 – rubenvb

+0

単語自体を変更していますか?それから、コピーが必要です。そうでなければ(ラップされた) 'shared_ptr ' – MikeMB

答えて

1

shared_ptr<Word>を使用してください。なぜなら、それはあなたが概念的に意味するものだからです。そしてそれは正しいことが証明されています。プログラムと比較して遅いと確信しているときにのみ、この低レベルの最適化を開始します(ヒント:そうではありません)。

< 1.は、概念的には分かりやすいインスタンスを共有しないことを意味します。もしあなたがその言葉の一つを変えたら?

< 3.スレッドセーフでないことを意味します。さて、ミューテックスを追加すると、DIY shared_ptr<>を実装することになります。サポートする必要がありますが、おそらく遅くなる/エラーが発生しやすくなります(テスターの数について考える - テスター! - 公式なもの)。努力する価値はありません。

+0

Downvoterを使用してください:この回答を改善する方法/間違っている点についてコメントしていただけますか? – lorro

+0

私は奇数の投票を心に残しません。人々は答えを好まないかもしれない理由のすべての種類があります。あなたがupvotesなしで2以上を取得した場合、それはおそらくあなたが何かを見逃しているというシグナルです。私はあなただと思っていますので、質問に答えて+1してください:) –

+0

@RichardHodges:まさに私が尋ねた理由です。もし私が何かを見逃してしまったら、私も学びたいと思っています:) - それが私にとってここの大きな利点です。また、他の人が私が何かを探しているときにやっているように、ここで適切な答えを見つけるようにしたい。だから私は尋ねました - 私は私と私の周りの世界を改善するとき、私は批評家に気にしません。 – lorro

0

これは達成しようとしている内容によって異なります。

スマートポインタを使用するソリューションは、各センテンスの単語を個別に変更したくない場合に適しています。

ディープコピーを使用する解決策も必要です。つまり、各単語を別々にコピーする必要があります(個別に変更することができます)。ただし、これにコピーコンストラクタを使用することを検討してください(または少なくとも削除してください)。

最初のインスタンスがコピー後に破棄されなければならないことを意味するので、ブールの解決策は本当に悪いです。しかし、boolをいくつかの共有refcounter(std :: shared_ptr)に置き換えると、これらのオブジェクトが作成後に完全に不変であるという仮定の下でのみ受け入れられます。

2

ポインタのコレクションを使用しないでください。ヒープに割り当てられた単語の実装が必要な場合は、値のような振る舞いを持つハンドルクラスで囲みます。

そして(ETC、またはベクター)リストに格納

例えばは:

struct word_impl {}; // big thing 

auto clone(const word_impl& impl) -> std::unique_ptr<word_impl> { 
    // clone could if you wished, defer to a virtual clone method 
    // on word_impl 
    return std::make_unique<word_impl>(impl); 
} 


struct word 
{ 
    // construct from letters 
    word(const char* letters) : impl_ { std::make_unique<word_impl>(letters) } { } 

    // let's make it copyable 
    word(const word& r) : impl_ { clone(r.impl_) } {} 

    word& operator=(const word& r) { 
     if (this != std::addressof(r)) { 
      impl_ = clone(r.impl_); 
     } 
     return *this; 
    } 

    // and moveable 
    word(word&& r) = default; 
    word& operator=(word&& r) = default; 

private: 

    std::unique_ptr<word_impl> impl_; 
} 

ここで両方の共有及び複製可能な単語の用語の単語オブジェクトを表す完全な、コンパイル例です。

私がここでやろうとしたことは、「共感」と実際の単語の操作との分離です。

これにより、クライアントサイトで同じコードを使用して、一意の単語または共有単語の文を作成することができます。ユーザーは、単語の内部動作やメモリの管理方法についての知識は必要ありません。彼が知る必要があるのは、それをコピーし、印刷し、比較することができることだけです(この場合)。

さらに、unique_wordをshared_wordに変換する操作もあります。

#include <memory> 
#include <iostream> 
#include <vector> 
#include <iomanip> 
#include <boost/operators.hpp> 

// 
// define some protocols that a handle class can inherit from. These 
// translate methods on the handle onto methods on the implementation 
// 
template<class Handle> 
struct implement_ostream_protocol { 
    friend std::ostream &operator<<(std::ostream &os, const implement_ostream_protocol &proto) { 
     auto &ref = static_cast<const Handle &>(proto).get_reference(); 
     return os << ref; 
    } 
}; 

template<class Handle, class Comp = std::less<>> 
struct implement_less_than_protocol { 
    friend bool operator<(const implement_less_than_protocol &l, const implement_less_than_protocol &r) { 
     auto &lr = static_cast<const Handle &>(l).get_reference(); 
     auto &rr = static_cast<const Handle &>(r).get_reference(); 
     auto comp = Comp(); 
     return comp(lr, rr); 
    } 
}; 

template<class Handle> 
struct implement_setValue_protocol { 
    template<class T> 
    decltype(auto) setValue(T&& value) 
    { 
     auto &lr = static_cast<Handle &>(*this).get_reference(); 
     return lr.setValue(std::forward<T>(value)); 
    } 
}; 

// 
// this is the implementation of a word 
// 
struct word_impl { 
    word_impl(const char *letters) : word_(letters) { 
     std::cout << "constructed word: " << word_ << std::endl; 
    } 

    word_impl(const word_impl &r) : word_(r.word_) { 
     std::cout << "copied word: " << word_ << std::endl; 
    } 

    word_impl(word_impl &&r) noexcept : word_(std::move(r.word_)) { 
     std::cout << "moved word: " << word_ << std::endl; 
    } 

    word_impl &operator=(const word_impl &r) { 
     if (this != std::addressof(r)) { 
      word_ = r.word_; 
      std::cout << "assigned word: " << word_ << std::endl; 
     } 
     return *this; 
    } 

    word_impl &operator=(word_impl &&r) noexcept { 
     if (this != std::addressof(r)) { 
      word_ = std::move(r.word_); 
      std::cout << "move-assigned word: " << word_ << std::endl; 
     } 
     return *this; 
    } 

    // some wordy operations 
    bool comes_before(const word_impl &r) const { 
     return word_ < r.word_; 
    } 

    void setValue(const char* p) 
    { 
     std::cout << "value changed from " << word_ << " to " << p << "\n"; 
     word_ = p; 
    } 

    // write myself 
    friend std::ostream &operator<<(std::ostream &os, const word_impl &r) { 
     return os << std::quoted(r.word_); 
    } 

    struct comes_before_op { 
     bool operator()(const word_impl &l, const word_impl &r) const { 
      return l.word_ < r.word_; 
     } 
    }; 

    std::string word_; 
}; // big thing 

// 
// these are the protocols I want all types of word handles to support 
// 
template<class Handle> 
struct word_impl_protocols 
     : implement_ostream_protocol<Handle>, 
      implement_less_than_protocol<Handle, word_impl::comes_before_op> , 
      implement_setValue_protocol<Handle>, 
      boost::less_than_comparable<word_impl_protocols<Handle>> 
{ 

}; 


auto clone(const word_impl &impl) -> std::unique_ptr<word_impl> { 
    // clone could if you wished, defer to a virtual clone method 
    // on word_impl 
    return std::make_unique<word_impl>(impl); 
} 


// 
// lets make a copyable word that clones its implementation 
// 
struct unique_word 
     : word_impl_protocols<unique_word> { 
    // construct from letters 
    unique_word(const char *letters) : impl_{std::make_unique<word_impl>(letters)} {} 

    // let's make it copyable 
    unique_word(const unique_word &r) : impl_{clone(*r.impl_)} {} 

    unique_word &operator=(const unique_word &r) { 
     if (this != std::addressof(r)) { 
      impl_ = clone(*r.impl_); 
     } 
     return *this; 
    } 

    // and moveable 
    unique_word(unique_word &&r) noexcept = default; 

    unique_word &operator=(unique_word &&r) noexcept = default; 

    word_impl const &get_reference() const { 
     return *impl_; 
    } 

    word_impl &get_reference() { 
     return *impl_; 
    } 

    // warning - destructive - provides a means to create a 
    // shared word from a unique_word 
    auto share() { 
     return std::shared_ptr<word_impl> {std::move(impl_)}; 
    } 

private: 

    std::unique_ptr<word_impl> impl_; 
}; 

// 
// and a word type that shares its implementation 
// 
struct shared_word 
     : word_impl_protocols<shared_word> { 
    shared_word(const char *letters) : impl_{std::make_shared<word_impl>(letters)} {} 

    shared_word(unique_word &&source) : impl_{source.share()} {} 

    const word_impl &get_reference() const { return *impl_; } 
    word_impl &get_reference() { return *impl_; } 

    std::shared_ptr<word_impl> impl_; 
}; 


int main() { 

    std::cout << "creating first sentence:\n"; 
    std::vector<unique_word> sentence1 = [] { 
     std::vector<unique_word> result; 
     result.emplace_back("abc"); 
     result.emplace_back("def"); 
     result.emplace_back("ghi"); 
     return result; 
    }(); 

    std::cout << "copying first sentence:\n"; 
    std::vector<unique_word> sentence2 = sentence1; 
    std::sort(sentence2.begin(), sentence2.end(), std::greater<>()); 

    std::copy(sentence1.begin(), sentence1.end(), std::ostream_iterator<unique_word>(std::cout, ", ")); 
    std::cout << std::endl; 

    std::copy(sentence2.begin(), sentence2.end(), std::ostream_iterator<unique_word>(std::cout, ", ")); 
    std::cout << std::endl; 

    std::cout << "converting first sentence to shared words:\n"; 
    std::vector<shared_word> sentence3; 
    for (auto& unique : sentence1) 
    { 
     sentence3.emplace_back(std::move(unique)); 
    } 
    std::copy(sentence3.begin(), sentence3.end(), std::ostream_iterator<shared_word>(std::cout, ", ")); 
    std::cout << std::endl; 

    std::cout << "copying sentence of shared words:\n"; 
    auto sentence4 = sentence3; 

    std::cout << "changing the first word of a shared word sentence:\n"; 
    sentence3.at(0).setValue("xyz"); 
    std::copy(sentence3.begin(), sentence3.end(), std::ostream_iterator<shared_word>(std::cout, ", ")); 
    std::cout << std::endl; 
    std::copy(sentence4.begin(), sentence4.end(), std::ostream_iterator<shared_word>(std::cout, ", ")); 
    std::cout << std::endl; 
} 

予想される出力:

creating first sentence: 
constructed word: abc 
constructed word: def 
constructed word: ghi 
copying first sentence: 
copied word: abc 
copied word: def 
copied word: ghi 
"abc", "def", "ghi", 
"ghi", "def", "abc", 
converting first sentence to shared words: 
"abc", "def", "ghi", 
copying sentence of shared words: 
changing the first word of a shared word sentence: 
value changed from abc to xyz 
"xyz", "def", "ghi", 
"xyz", "def", "ghi", 
+0

理由を説明できますか?なぜ、ポインタのコンテナではなく、ptrまたは値を保持するかどうかを柔軟に選択する代わりに、ユーザーにラッパーを強制するのはなぜですか? (そして、すべてのクラスのラッパーを書く理由はオーバーヘッドのようです。) – lorro

+0

@loroの値はポインタよりも簡単です。特にコンテナでは、生涯と比較のコンセプトが非常に頻繁に必要です。あなたの場合、クローンについて言及しました。ユーザーには、クローンプロトコルの知識を強制するよりも、通常のコピーセマンティクスを与えるほうが賢明です。将来、クローニングの仕方を変更することもできます。その結果、すべてのユーザーコードに変更を強制するのは残念です。 –

+0

しかし、OPが価値の回路図を期待していることをどのように確認できますか? OPが些細なスペルチェッカー(間違った単語を正しいものに置き換えます)を構築したい場合はどうなりますか? 'word'の' string'は可変です。つまり、同じ単語を2つの文に割り当てて単語自体を変更すると、両方が変更されると思います。 – lorro