2012-12-11 43 views
1

我应该得到相同的两行.. 发生什么事,我得到两个不同的值..就像是针对不同的岗位.. 我认为错误是里面的d->add(*b)C++向量失去指针引用

输出

thiago 14333804 
    Ph¿├┌ 2816532 

来形容的话更好,我把下面

我有一个程序代码

int main(int argc, char **argv) { 

    CClass* c = new CClass(); 

    BClass* b = c->getNext(); 

    printf("%s %d \n", b->getValue(), b->getValue()); 

    DClass* d = new DClass(); 
    d->add(*b); 

    printf("%s %d \n", d->getNext(), d->getNext()); 

    cin.get(); 

    return 0; 
} 

的接口都在下面

class BClass 
{ 
private: 
    char* value; 
    bool stale; 
public: 
    BClass(char* value); 
    ~BClass(void); 
    char* getValue(); 
    bool isStale(); 
}; 


class CClass 
{ 
private: 
    vector<BClass*> list; 
public: 
    CClass(void); 
    ~CClass(void); 

    BClass* getNext(); 
}; 


class DClass 
{ 
private: 
    vector<BClass*> list; 
    static bool isStale(BClass* b) { return b->isStale();}; 
public: 
    DClass(void); 
    ~DClass(void); 
    void add(BClass s); 
    char* getNext(); 
}; 

和实现如下

//BClass 

BClass::BClass(char* value) 
{ 
    this->value = value; 
    this->stale = false; 
} 

BClass::~BClass(void) 
{ 
} 

char* BClass::getValue() 
{ 
    return value; 
} 

bool BClass::isStale() 
{ 
    return stale; 
} 



//CClass 

CClass::CClass(void) 
{ 
    list.push_back(new BClass("thiago")); 
    list.push_back(new BClass("bruno")); 
    list.push_back(new BClass("carlos")); 
} 


CClass::~CClass(void) 
{ 
} 

BClass* CClass::getNext() 
{ 
    return list.at(0); 
} 

//DClass 

DClass::DClass(void) 
{ 
} 

DClass::~DClass(void) 
{ 
} 

void DClass::add(BClass s) 
{ 
    list.push_back(&s); 
} 

char* DClass::getNext() 
{ 
    BClass* b = list.at(0); 

    return b->getValue(); 
} 
+1

指针地址会有所不同,但什么指针指向可以相同 – dchhetri

+0

的地址,你传递一个对象,以添加副本而不是指针。这就是你得到一个位于栈上的临时对象的地址,它的作用域在add()函数内部。 – gregory561

回答

5

当您在B类的一个实例传递到D::add()功能您创建对象的深层副本和复制是放在堆栈上的东西。稍后,您使用该副本的地址将其推入列表中。一旦功能完成,这个自动变量超出了范围,因此你用来放入列表的指针不再有效。

要解决改变你的接口,以避免深拷贝如下:

void DClass::add(BClass * s) 
{ 
    list.push_back(s); 
} 

步骤一步的你的代码是做

  1. BClass* b = c->getNext(); //你的地址列表中的第一个元素(在构造函数中创建)并将其分配给b
  2. d->add(*b); // the * b将引用指向b的对象ÿb并把它入堆栈以准备调用add()
  3. void DClass::add(BClass s){ //一个解除引用的对象的深拷贝被放入该函数的堆栈帧
  4. list.push_back(&s); //的的那临时拷贝的地址原始对象正被用于添加到您的列表中
  5. } //这就是乐趣发生的地方 - 一旦函数完成,它将展开堆栈备份,之前由该临时副本占用的内存将被重新 - 用于其他目的。在你的情况下 - 它将用于传递参数到函数d->getNext()(总是有一个隐藏的这个参数给非静态成员函数),后来到printf()函数。记住 - 你以前的指针指向临时副本仍然指向到堆栈,但它现在由不同的数据占用,导致你看到腐败

一般的经验法则 - 从未使用指针来临时对象;-)

+0

@YePhlicK是不是有我保证,即使在这个功能范围之后s的地址总是有效的? – thiagoh

+0

只有地址在堆或者如果你正在做一些真的* *时髦的黑客攻击,并确保堆栈帧没有被重复使用(这是在他们的脑子应该想过这样做的一个非常重要的任务,没有人它,除了非常特殊的应用程序,如反调试mesures) – YePhIcK

1

DClass::add函数中,BClass s是一个局部变量。

void DClass::add(BClass s) 
{ 
    list.push_back(&s); 
} 

当你调用d->add(*b);,你是按值传递一个BClass,这意味着你就创建了一个副本,该副本的地址是不是原来的地址相同。

s会尽快走出去的范围为函数返回,并指向它的指针将是无效的。所以存储这个指针对你来说并不好,因为解引用它将是未定义的行为。

+0

是不是有无论如何保证's'的地址即使在这个函数范围之后总是有效的? – thiagoh

+0

据我所知,没有什么可以做的,以避免's'本身超出范围。你可以编写一个构造函数来复制这个对象并创建一个'new BClass(s​​)',并存储这个指针,但是我认为你想要用另一个解决方案,只需将'add(BClass s)'改为'添加(BClass * s)'并传递指针而不是复制。 – filipe