【问题标题】:Inheritance and derived attribute disappearing in C++继承和派生属性在 C++ 中消失
【发布时间】:2013-10-25 09:49:00
【问题描述】:

我对继承的概念很陌生,对 C++ 也很陌生,所以我的问题可能真的很愚蠢...

class A {
    public :
        A() {}
        A(string name) {name_ = name}

    private :
        string name_;
}

class B : public A {
    public :
        B() {}
        B(string name, int number) {
            name_ = name;
            number_ = number;
        }

    private :
        string name;
        int number;
}

class C {
    public :
        C() {}
        void addClass(int id, A* a) {
            map[id] = a;
        }

    private :
        Hash_Map<int, A*> map;
}

void main() {
    C* c = new C();
    for (int i = 0; i < 10; i++) {
        B* b = new B("randomName", 50);
        c->addClass(i, b); //1st problem
        delete b;            //2nd problem
    }

}

第一个问题:“c”中的“map”不应该保留“B”类的属性“number”吗?我知道我把 A* 放在参数中,但是如果我有几个从 A 派生的类,我应该怎么做?

第二个问题:当我删除“b”时,“map”中的所有属性似乎都获得了随机值。我猜问题是我需要将“b”复制到一个新对象中,但是如果我有“A*”作为我的 addClass() 参数,我该怎么做呢?我的指针 b 好像转换成了它的父类

编辑:不得不更改我忘记的代码中的几件事...

【问题讨论】:

  • 它确实保留了number,但它是私有的,我建议将其设为保护

标签: c++ pointers inheritance hashmap delete-operator


【解决方案1】:

一堆问题:

1) 类声明中没有初始化成员! (您的编辑解决了这个问题)

class B : public A {
public :
    B() : name("A"), number(0) {} // initialize in constructor. that's what they are for!

private :
    string name;
    int number;

}

(对 A 的声明也重复)

2) 您正在地图中存储指向A 的指针的副本(作为参数传递给addClass),而不是实际的对象。 因此,您的地图包含: 100 -> 指向 b 的指针

然后删除 b 指向的任何内容。你认为map[100] 现在包含什么? 指向垃圾的指针!所以,不要删除外面的指针!让 C 来处理。

3)(我之前的回答有一个明显的错误,有人投票赞成。所以,我会保留前面的部分并指出我的错误) 除非需要,否则不要使用指针。为自己节省一些工作。去弹吉他或阅读 Herb Sutter 的一些文章吧!

void main() {
   // don't use pointers and require that you delete them (unless you need to)
   B b; // default constructor is called automatically. it is destroyed for you, by the compiler
        // at the end of its scope (in this case, closing brace of main() )

   C c; 
   c.addClass(100, b);
}

让我们也修复 C。我们可以摆脱那些讨厌的指针吗?

class C {
public :
    C() {}
    void addClass(const int id, const A a) { // use const, its a good habit!
        map[id] = a;
    }

private :
    Hash_Map<int id, A a> map;

}

现在,这有什么问题?不仅仅是额外的副本;当您通过值将b 作为参数传递给addClass 时,编译器将复制b! So, we lostb` 的数据的A 部分(并覆盖)!

所以,我们绝对必须使用指针(引用是危险的,因为它们在范围退出时被删除)。

重要的是你让 C 拥有删除的所有权。

所以你的代码现在看起来像:

class C {
public :
    C() {}
    ~C() {
       for(pair<int, A*>& p : map) // C++11 syntax, yay!
          delete p.second; // here's where you clean up. not in main.

    }
    void addClass(const int id, const A* a) {
        map[id] = a;
    }

private :
    Hash_Map<int, A*> map;
}

void main() {
    B* b = new B(); // back to square 1!
    C c;
    c.addClass(100, &b);
} // no memory leaks

但我讨厌你说的删除..别担心,我们有 shared_ptr!

#include <memory>
using namespace std;
typedef shared_ptr<A> Aptr;

class C {
public :
    C() {}
    ~C() {
        cout << "Drinking a beer coz i use shared_ptr";     
    }
    void addClass(const int id, Aptr& a) {
        map[id] = a;
    }

private :
    Hash_Map<int, Aptr> map;
}

void main() {
    Aptr b(new B());
    C c;
    c.addClass(100, b);
} // still no memory leaks

希望对您有所帮助。

【讨论】:

  • 不幸的是,我的例子很糟糕。我不得不在我的程序中使用指针,但在这篇文章中试图过分简化它......
  • 使用 shared_ponters(或智能指针),以免丢失对象。使用共享指针后,无需调用 delete。如果你的 map > 持有 A 的共享指针,你就不用担心它们被删除了。
  • 哦,是的,如果可以,请使用共享指针。如果没有,找出在哪里清理。 RAII 是一个伟大的原则——在你的析构函数中进行清理。
猜你喜欢
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 2017-05-07
  • 2022-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多