2011-09-27 31 views
8

Bu imkansız olabilir, ama orijinal ifadesini geçmişten beri geçici tutmak mümkün olup olmadığını merak ediyordum. Ben ana nesneleri işaret nesnelerin zinciri var ve bir alt nesne yaratacak üye işlevi, basitleştirilmiş bir örnek buradaGeçici olarak ömrünü uzatmaktan kaçının mı?

class person{ 
    string name; 
    person * mommy; 
public: 
    person(const string & nam, person * m = 0) : name(nam), mommy(m) {} 
    person baby(const string & nam){ 
     return person(nam, this); 
    } 
    void talk() const{ 
     if (mommy) mommy->talk(); 
     cout << name << endl; 
    } 
}; 

int main(){ 
    person("Ann").baby("Susan").baby("Wendy").talk();  // fine 

    const person & babygirl = person("Julie").baby("Laura"); // not fine 

    babygirl.talk(); // segfault 
    return 0; 
} 

Ben person onu işleve geçmektir kullanmak istediğiniz yoludur ve bunun gibi bir şey:

void use(const person & p) { 
    p.talk(); 
} 
use(person("Anna").baby("Lisa")); 
İyi.

Bu, geçici ifadelerin hiçbirinin özgün ifadeyi geçmediği sürece işe yaramaz, ancak son geçici noktalardan birini bir const referansına bağlarsam, ebeveynleri hayatta kalmaz ve bir segfault alır. person'un kopya yapıcısını ve atama işlecini gizleyebilirim, ancak bu tür bir hatanın olmasını engelleyebileceğim herhangi bir yol var mı? Mümkünse dinamik ayırmayı önlemek isterim.

+0

@Konrad: Ironic; -] – ildjarn

+1

Bu kodun aynı şekilde "iyi değil" olduğunu unutmayın. const int & i = std :: vector yazıyor (1) 'vektör' bunu yazmayı bırakmaz ve buna gerek yoktur. Buradaki anahtar, anneyi yok etmenin bebeği kullanılamaz hale getirmesi, bebeğin annenin bir parçası olması. Tasarımda yanlış olan bu, karşı sezgiseldir. Yetim gibi bir şeylerin varlığını önleyerek bunu düzeltmeye çalışıyorsunuz, bu da uygun olabilir ama aynı zamanda yetimlerin daha iyi tanımlanmış bir davranışa sahip olup olmadığını veya daha açık bir şekilde yaratılması gereken kötü bir şey olup olmadığını da düşünmelisiniz. –

+0

Steve ile anlaştı: Bu sadece kötü tasarım gösteriyor.Çıplak bir işaretçinin ortaya çıkması, bir şeyin kapalı olduğunu vermelidir. –

cevap

3

Burada, çocukların ebeveynleri için göstergelerin bulunduğu bir veri yapısı oluşturuyorsunuz gibi görünüyor. Bu durumda zamansızlıkları kullanmak size keder getirecektir. Bunu güvenli hale getirmek için, bir çeşit referans sayımını dinamik olarak tahsis etmeniz ve kullanmanız gerekecektir.

boost::shared_ptr numaralı telefonu kullanmayı düşündünüz mü? Bir referans sayılan akıllı işaretçi sınıfının bir uygulamasıdır. shared_ptr'u ve belki bazı fabrika yöntemlerini kullanarak, istediğiniz efekti elde edebilir ve dinamik bellek ayırma ağrısını azaltabilirsiniz. Denedim ve işe yaramış görünüyor. Kod kapsam dışına çıktığında, paylaşılan_ptr'lere bırakılan referans olmadığından nesnelerin tümü yok edilir.

Düzenleme: Kök nesne veri yapının ömrünü kontrol edebilmesini sağlayacak şekilde karşılık zounds' comment , ben örnek değiştirdiniz.

#include <iostream> 
#include <string> 
#include <vector> 
#include <boost\shared_ptr.hpp> 
#include <boost\weak_ptr.hpp> 

using boost::shared_ptr; 
using boost::weak_ptr; 

using std::string; 
using std::cout; 
using std::endl; 
using std::vector; 

class person; 
typedef shared_ptr<person> Person; 
typedef weak_ptr<person> PersonWk; 

class person {  
    PersonWk pThis; 
    friend Person makePerson(const string & nam, Person m = Person()); 

    string name; 
    PersonWk mommy; // children should not affect parent lifetime, so store weak ptr 
    vector<Person> children; // parents affect children lifetime so store child shared ptrs 

    // make constructor private so that you can only make a person using factory method 
    person(const string & nam, Person m) : name(nam), mommy(m) 
    { 
     // for demo purposes 
     printf("creating %s\n", nam.c_str()); 
     ++personCount; 
    } 

    // undefined copy constructor and assignment operators 
    person(const person&); 
    person& operator=(const person&); 

public: 
    // for demo purposes 
    static int personCount; 

    ~person() 
    { 
     // for demo purposes 
     printf("destroying %s\n", name.c_str()); 
     --personCount; 
    } 

    Person baby(const string & nam){   
     Person child = makePerson(nam, Person(pThis)); 
     children.push_back(child); 
     return child; 
    } 

    void talk() const{ 
     if (Person mom = mommy.lock()) 
      mom->talk(); 
     cout << name << endl; 
    } 
}; 

int person::personCount = 0; 

// factory method to make a person 
Person makePerson(const string & name, Person m) { 
    Person p = Person(new person(name, m)); 
    p->pThis = p; // stash weak_ptr so I can use it to make a shared_ptr from "this" in the baby method 
    return p; 
} 

void use(const Person p) { 
    printf("In method use...\n"); 
    p->talk(); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    printf("personCount=%d\n", person::personCount); 
    { 
     Person ann = makePerson("Ann"); 

     // ann has baby and grandbaby, pass grandbaby to use method 
     use(ann->baby("Susan")->baby("Wendy")); 

     ann.reset(); // remove reference from root object. Destruction ensues... 
    } 
    printf("personCount=%d\n", person::personCount); 
    return 0; 
} 
+0

Cevabınız için teşekkürler. Paylaşılan ptr ile benim için sorun, kök nesnenin belirli bir yıkım noktasına sahip olması gerektiğidir ve bunun bir yerde saklanmış bir işaretçi olabileceğinden endişelenmek istemiyorum. Sadece bir işlev çağrısı olduğu sürece var olan geçici noktalara ihtiyacım var ve sonra ölebilirler. Çocukların ebeveynlerini etkilemesine izin verilmeyen bir çocuk-> ana yapı kullanıyorum (herkesin kullanmasına izin verilen normal genel arayüz dışında) – zounds

+0

Kök nesnesini bilinen bir konumda depolamak bir yaklaşım olabilir. Daha sonra, her ebeveynin çocuklarına shared_ptrs yazmasını sağlayın ve çocuklar ebeveynleri için zayıf_ptrileri tutabilir. Sonra kök nesnesini NULL olarak ayarladığınızda (böylece tüm referanslar kaldırılır), ağaç yukarıdan aşağıya doğru yok edilir. –

+0

Önceki yorumumda yaklaşımı kullanmak için değiştirilmiş kod örneği. –

0

Böyle bir şey yapmanız gerekecek:

void use(const person & p) { 
    p.talk(); 
} 
person a("Anna"); 
use(a.baby("Lisa")); 

Eğer gerçekten sonra (onunla bitene kadar, ebeveyn "a" kapsam dışında gitmez Bu şekilde "kullan" için çağrı yap. Orijinal kodla ilgili sorun, "Anna" ebeveyni sadece "bebek" i çağırmak için yeterince uzun süre kalmak zorundadır ve ebeveyn, işlev çağrısı yapmadan önce atılabilir. Ebeveyni kapsam ile bir değişken yaparak, imha edildiğinde kontrol edebilirsiniz.

Bu bana tehlikeli geliyor mu? Evet. Bu nedenle, m-sharp'ın dinamik ayırma hakkındaki cevabına bir göz atmanızı tavsiye ederim. Ama eğer referans saymaya ihtiyaç duymayan bir yöntem istiyorsanız, o zaman bunu bu şekilde yapabilirsiniz ...