2017-07-19 12 views
2

上のオブジェクトを扱う私は現在のVisual Studioに2017年C++は、ヒープ

を使用して、C#/ Javaの背景から来て、C++を学んでいる私は、ヒープ上のオブジェクトを作成し、適切に道の下にそれらを参照に関しては疑問を持っています。これまでは、私は複数のチュートリアルや方法を見つけました。可能な限りスマートポインタを使用することをお勧めする人もいれば、the devils toolを誓う人もいます。このような

私の現在の主なルックス:

//Main 
Person *makePerson() 
{ 
    string name; 
    int age; 
    cout << "Input name: "; 
    cin >> name; 
    cout << "Input age: "; 
    cin >> age; 
    return new Person(name, age); 
} 

Child *makeChild(Person &parent) 
{ 
    return new Child(*makePerson(), &parent);; 
} 

int main() 
{ 
    cout << "---Input parent data---" << endl; 
    Person *person = makePerson(); 
    cout << "printing: " << *person << endl; 
    cout << "---Input child data---" << endl; 
    Child *child = makeChild(*person); 
    cout << "printing: " << *child << endl; 
    cout << "---end of main---" << endl; 
    delete person; 
    delete child; 
    return 0; 
} 

機能は、個人データの入力を処理し、新しいPersonオブジェクトへのポインタを返します。次に、親参照を取り、残りのデータをmakePersonに問い合わせることで、子オブジェクトの作成を処理する関数を持っています。

これは良いC++と考えることができますか?どうすればそれをより良くすることができますか?私はいくつかのコード例を本当に感謝します。

すでに提案されているように、生ポインタをshared_ptr<Person> person(重い)またはunique_ptr<Person>(より共有されたもの)に置き換えることができます。

これは、PersonクラスとChildクラスのコードです。 Childには、タイプPerson *parentの生ポインタがあることに注意してください。

//header 
class Person 
{ 
protected: 
    std::string name; 
    int age; 
public: 
    Person(); 
    Person(const Person& other); 
    Person(std::string inName, int inAge); 
    ~Person(); 
    virtual void print() const; 
    std::string getName() const; 
    int getAge() const; 
    Person &operator=(const Person &other); 
    //overload print functionality, act as if it was toString 
    friend std::ostream &operator<<(std::ostream &out, const Person &p); 
}; 
//cpp 
Person::Person() : name(""), age(0) { 
    std::cout << "Person empty constructor" << std::endl; 
} 

Person::Person(std::string inName, int inAge) : name(inName), age(inAge) { 
    std::cout << "Person (" << name << ") default constructor" << std::endl; 
} 

Person::Person(const Person & other) : name(other.name), age(other.age) { 
    std::cout << "Person (" << name << ") copy constructor" << std::endl; 
} 

Person::~Person() { 
    std::cout << "Person (" << name << ") destructor" << std::endl; 
} 

void Person::print() const { 
    std::cout << name << ", " << age << std::endl; 
} 

std::string Person::getName() const 
{ 
    return name; 
} 

int Person::getAge() const 
{ 
    return age; 
} 

Person & Person::operator=(const Person & other) { 
    std::cout << "Person (" << other.name << ") assignment constructor" << std::endl; 
    name = other.name; 
    age = other.age; 
    return *this; 
} 
std::ostream &operator<<(std::ostream &out, const Person &p) { 
    return out << p.name << ", " << p.age; 
} 

子供は人であり、子供が誰の子供の親であるかを知ることは理にかなっています。しかし、この "知識"をどのように扱うかは不明です。ここで私は子クラスのために使用していたコードは次のとおりです、私は私の質問が残っていると仮定し

enter image description here

誰も私にそれが必要かの例を与えることができる:

//Header 
class Child : public Person 
{ 
private: 
    const Person *parent; 
public: 
    Child(); 
    Child(std::string name, int age); 
    Child(std::string name, int age, const Person *parent); 
    Child(const Child &child, const Person *parent); 
    Child(const Person &person); 
    ~Child(); 
    Child &operator=(const Child &other); 
    void print() const; 
    friend std::ostream &operator<<(std::ostream &out, const Child &c); 
}; 
//cpp 
Child::Child() { 
    std::cout << "Child empty constructor" << std::endl; 
} 

Child::Child(std::string name, int age) : Person(name, age), parent(nullptr) { 
    std::cout << "Orphan (" << name << ") constructor" << std::endl; 
} 

Child::Child(std::string name, int age, const Person *parent) : 
    Person(name, age), parent(parent) { 
    std::cout << "Child (" << name << ") default constructor" << std::endl; 
} 

Child::Child(const Child &child, const Person *parent) : 
    Person(child.name, child.age), parent(parent) { 
    std::cout << "Child (" << child.name << ") copy constructor" << std::endl; 
} 

Child::Child(const Person &person) : Person(person), parent(nullptr) { 
    std::cout << "Child from person (" << name << ") constructor" << std::endl; 
} 

Child::~Child() { 
    std::cout << "Child (" << name << ") destructor" << std::endl; 
} 

Child & Child::operator=(const Child & other) { 
    name = other.name; 
    age = other.age; 
    parent = other.parent; 
    std::cout << "Child (" << name << ") assignment constructor" << std::endl; 
    return *this; 
} 

void Child::print() const { 
    if(parent) 
     std::cout << *this << " is child of " << *parent << std::endl; 
    else 
     std::cout << *this << " is orphan" << std::endl; 
} 

std::ostream &operator<<(std::ostream &out, const Child &c) { 
    return out << c.name << ", " << c.age << " is " << 
     (c.parent ? ("child of " + c.parent->getName() + ", " + std::to_string(c.parent->getAge())) : "orphan"); 
} 

は、これは私が手に出力されC++と見なされるように見えますか?

@ user4581301あなたが更新されたメインを見れば、* (raw pointer)の代わりにstd::unique_ptrを返さなければならないのですか?その場合には、私の関数は次のようになりますよう

std::unique_ptr<Person> makePerson2() 
{ 
    string name; 
    int age; 
    cout << "Input name: "; 
    cin >> name; 
    cout << "Input age: "; 
    cin >> age; 
    return std::unique_ptr<Person>(new Person(name, age)); 
} 

そして、変数宣言:

std::unique_ptr<Person> upParent = makePerson2(); 
cout << "printing: " << *upParent << endl; 

することは、これは私がこれまで持っているものよりもC++「より良い」と考えることでしょうか?

+5

はい、 'std :: shared_ptr'は重いですが、必要な時間はかなり短いです。ほとんどの場合、 'std :: unique_ptr'はあなたが本当に望むものであり、ほとんどの場合、それはゼロのオーバーヘッドです。 – NathanOliver

+1

'string * s =新しい文字列(" Hello world ");'ほとんどの場合、std :: stringでこれをしたくありません。 – drescherjm

+1

編集と 'makePerson2'について:はい。それは良いでしょうが、もしあなたがC++ 14コンパイラを持っていれば '' make_unique'(http://en.cppreference.com/w/cpp)として 'return std :: make_unique (name、age);/memory/unique_ptr/make_unique)は[いくつかの可能性のあるエラーの場合]を排除します(https://stackoverflow.com/questions/37514509/advantages-of-using-make-unique-over-new-operator) – user4581301

答えて

5

私はあなたは、Java/C#の背景から来る場合は特に約C++ を知るべき最も重要なことの一つだと思います:

オブジェクトは、デフォルトで値-種類あるタイプを参照しません!コードは何になってどのように

int main() 
{ 
    //this work 
    Person person("John Doe", 22); 
    //this work 
    Child child("Johnny Doe", 2, person); 
    cout << "---end of main---" << endl; 
    return 0; 
} 

を参照してください:

あなたは全体のコードは、単にとして書かれているだろうか? オブジェクトは参照型ではないので、割り当てについて心配する必要はありません。

ルールの私の個人的な階層が行く:

  1. 使用のオブジェクトを可能な限り値の型として。コピーを避けるために参照渡ししてください。関連クラスに対して有効な移動コンストラクタが実装されていることを確認してください。オブジェクトは値型+参照としてC++をプログラミングするデフォルトの方法でなければなりません。
  2. 参照を使用できない場合は、欠落しているオブジェクトを指定するために、C-pointerを使用します。いずれにしても、Cポインタを最小限に厳密にし、何も管理できないようにします。 Cポインタは基本的には何かに対する "ビューア"であり、 "何もない"(またはnull)を見ることができます。あなたはC + +の参照でCのポインタを置き換えることができる場合は常に考えています。 の場合は、doです。
  3. std::unique_ptr何らかの理由で動的多形性などの動的メモリ割り当てが必要な場合は、 C++は、Javaスタイルの継承+オーバーライド手法ではなく、静的な多形性として主にテンプレートで機能することに注意してください。
  4. std::shared_ptrを使用することはほとんどありません。異なるスレッドで参照されているオブジェクトのような所有者が多数いると確信している場合に限ります。極端な場合にはstd::shared_ptrを使用してください。常に共有ポインタをコピーしてください。参照によって共有ポインタを渡すことはありません。

とにかく、newnew[]deletedelete[]はほとんど廃止されました。非常に極端な場合には、図書館の作家だけがそれらを使うべきです。 std::vectorおよびstd::listなどのSTLコンテナの横にヒープにオブジェクトを割り当てる唯一の方法はstd::make_である必要があります。

+0

あなたが提案したコードが私にとってうまくいくとは思わない。 更新されたメインについては私のポストを参照してください。 – AleksanderNaumenok

+0

私は、Cポインタを使う前に、最近の(C++ 11/14/17)の時点で、 'std :: unique_ptr'の後ろに' std :: shared_ptr'を使用する方が一般に良い方法だと考えています。どちらもタイプセーフであり、メモリが誤って管理されているときに問題を引き起こす可能性は非常に低くなります。ユニークなポインタは、Cポインタよりもデータの所有権が優れています。 –

+0

@AleksanderNaumenokダイナミックアロケーションを明示的に試していても、読書を続けているなら、コードはうまくいかないでしょう。下の部分は、さまざまな割り当て方法を使用する順序を正確に示しています。 1と2はあなたの要件が動的割り当てであるために出ています。それはオプション3に、3はあなたのケースに完全に合っています。 – user4581301