【问题标题】:C++ implementing derived class from base class by map and using methods fromC++ 通过映射从基类实现派生类并使用来自
【发布时间】:2019-06-02 17:25:12
【问题描述】:

我面临的问题是我想创建抽象类和 4 个子类。 Abstract class= Entry ,其他子类在代码中给出。

class entry {
    friend ostream &operator<<(ostream &os, const entry &obj);

private:
    static constexpr char *def_info = "Undefind";
protected:
    string desc;
    int ID;
    static int counter;
public:
    entry() : entry(def_info) {}

    entry(string input_s);

    void setDesc(string);

    string getDesc() const;

    int getID() const;

    virtual string getContents() const = 0;

    virtual void displaying(ostream &os) const = 0;

    virtual ~entry() = default;
};

int entry::counter = 0;

entry::entry(string input_s) :
        desc{input_s} {
    ++counter;
    ID = counter;
}

ostream &operator<<(ostream &os, const entry &obj) {
    os << obj.getContents()<<endl;
    obj.displaying(os);
    return os;
}

void entry::setDesc(string input) {
    desc = input;
}

string entry::getDesc() const {
    return desc;
}

int entry::getID() const {
    return ID;
}

//PhoneEntry extending the Entry with a phone number
class phone_entry :virtual  public entry {
private:
    static constexpr char *text = "Undefind";
    static constexpr int def_no = 48000000000;
protected:
    int phone_number;
public:
    phone_entry(string input_s = text, int input = def_no) :
            entry(input_s), phone_number(input) {}

    virtual string getContents() const override {
        ostringstream output;
        output << "ID: " << ID << "\nD: " << desc << "\nPhone number : " << phone_number;
        return output.str();
    }

    virtual ~phone_entry() = default;
};

//EmailEntry extending the Entry with an e-mail addres
class email_entry : virtual public entry {
private:
    static constexpr char *def_info = "Undefind";

protected:
    string email;
public:
    email_entry(string des = def_info, string email_entry = def_info) :
            entry(des), email{email_entry} {}

    virtual string getContents() const override {
        ostringstream output;
        output << "ID: " << ID << "\nD: " << desc << "\nEmail : " << email;
        return output.str();
    }

    virtual ~email_entry() = default;
};
//AddressEntry extending the Entry with an address containing a city, a street and a house number

class address_entry : virtual public entry {
private:
    static constexpr char *def_info = "Undefind";
    static constexpr int def_no = 0;
protected:
    string city;
    string street;
    int house_number;

public:

    address_entry(string des = def_info, string c = def_info, string s = def_info, int hn = def_no) :
            entry{des}, city{c}, street{s}, house_number{hn} {}

    virtual string getContents() const override {
        ostringstream output;
        output << "ID: " << ID << "\nD: " << desc << "\nCity: " << city << "\nStreet: " << street << "\nHouse number: "
               << house_number;

        return output.str();
    }

    virtual ~address_entry() = default;
};

class contact_book : virtual public entry {
    static constexpr char *def_info = "Undefind";
private:
    map<string,entry *> contacts;
    string nick_name;
public:
    class error_mesg : public logic_error {
    public:
        error_mesg(const string message = "NULL") :
                logic_error(message) {}
    };

    contact_book(string e = "undefind") :
            entry{def_info},nick_name{e} {}

    void add(string a , entry &content){
        contacts.insert(make_pair(a,&content));
    }


    virtual void displaying(ostream &os) const override {
        os << "TESTING";
    }

};

我想使用 contact_book 类并使用 map 容器通过使用 add 方法将键和值插入到任何子类中,例如 ("cool", new phone_entry("NAME",000000),它有两个参数,一个用于键,另一个必须连接到另一个类,所以我认为使用条目 obj 将完成我在成员中使用指针的工作,因为我想使用多态性。我也不知道如何使用 getContents,它知道从哪个子类调用。例如,他要求 main 看起来像这样:

    ContactBook contacts(“My contacts”);
contacts.add(“Johny”, new PhoneEntry(“John Smith”, 100200300));
contacts.add(“Lisa”, new EmailEntry(“Lisa Wood”, “lisa@gmail.com”));
contacts.add(“Andy”, new AddressEntry(“Andrew Fox”, “Warsaw”, “Green St.”, 7));
cout << contacts;
//result (ordered):
//Andrew Fox: Warsaw, Green St. 7 (pos. 3)
//John Smith: phone 100200300 (pos. 1)
//Lisa Wood: e-mail lisa@gmail.com (pos. 2)
try {
 cout << contacts[“Andy”].getContents() << endl;
 //result:
 //Andrew Fox: Warsaw, Green St. 7 (pos. 3)

你能帮我弄清楚如何实现它们并得到他想要的东西,以及如何在添加方法和新函数中工作。

【问题讨论】:

  • entry(des), email{email_entry} 可以争论统一初始化是否是一件好事(我强烈反对...),但请 - 决定一个(始终贯穿整个文件/项目),不要'不要混合......
  • 为什么contact_book会继承自entry

标签: c++ oop polymorphism abstract-class implementation


【解决方案1】:

您不太清楚您的实际问题是什么——所以我假设您无法获得整个通讯录所需的输出...

首先,还有一些其他问题:

contact_book 继承自entry 毫无意义——这本书包含 条目,它不是一个。似乎您只继承了能够覆盖displaying 函数,以便能够将已经定义的operator&lt;&lt; 用于entry 类。但这是继承的一个不好的理由。相反,您应该为您的通讯录提供一个单独的 operator&lt;&lt; 重载:

std::ostream& operator(std::ostream& s, contact_book const& cb); // coming to later

然后习惯于在适当的地方重用代码;我假设getContents 应该输出与通过operator&lt;&lt; 写入std::cout 相同的字符串。好的,那么让我们从中获利:

std::string entry::getContents()
{
    std::ostringstream s;
    s << *this;
    return s.str();
}

您不再需要将其虚拟化(也不应该)。

我个人会为 displaying 函数使用默认值(不过,这不是一个很好的名字,你应该更喜欢命令式形式 'display',也许更好:'printToStream' 或只是 'printTo'):

void entry::printTo(std::ostream& s)
{
    // output members to s 
}

然后你的继承类可以重用它:

void phone_entry::printTo(std::ostream& s)
{
    entry::printTo(s); // use parent's version first to print common data
    // now append phone number to s... 
}

不过,用户应该使用operator&lt;&lt; 而不是printTo 函数。保护它(甚至是私有的)可能是个好主意。

终于回到输出通讯录了:

std::ostream& operator(std::ostream& s, contact_book const& cb)
{
    // you want to output every entry? then do so:
    for(auto& e : cb.contacts)
    {
        s << *e.second << std::endl;
        //     ^^^^^^^ map entries are of type std::pair<key_type const, mapped_type>
        //   ^ you stored pointers, operator<< accepts reference
    }
}

同样,您将重复使用已经编写的代码。

最后一点:您混合了经典初始化(括号)和统一初始化(大括号),一次甚至在一行中(email_contact):

entry(des), email{email_entry}

我个人认为 UI一般 有一些合理的推理,但它在 C++ 中实现的方式被破坏了。还有很多其他问题,我最喜欢的一个是:

std::vector<int> v0{7};   // gives you a vector with 7 elements, all 0!!!
// with classic initialization:
std::vector<int> v1(7);   // now clear, you WANT such a vector!
std::vector<int> v2({7}); // now clear, you want initializer list

您现在可以遵循我的推理或不遵循我的推理(其他人选择 UI 仍然是为了从其优势中获利),但无论您做出什么决定 - 请坚持始终 覆盖整个文件/项目!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-24
    • 2019-07-16
    • 1970-01-01
    • 2018-06-28
    • 2011-03-23
    • 1970-01-01
    相关资源
    最近更新 更多