C ++ реализует производный класс от базового класса по карте и использует методы из - PullRequest
0 голосов
/ 08 января 2019

Я столкнулся с проблемой, которую хочу создать абстрактный класс и 4 подкласса. Абстрактный класс = 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 и контейнер контейнера для вставки ключа и значений в любой из подклассов, таких как ("cool", new phone_entry("NAME",000000), используя метод add, который имеет два параметра: один для ключа, а другой - для соединения с другим. классы, поэтому я подумал, что использование записи obj сделает всю работу. Я использовал указатель в члене, потому что я хочу использовать полиморфизм. Кроме того, я понятия не имею, как я могу использовать getContencts, и он знает, из какого вызова подкласса. Например, он спросил, что главное будет выглядеть так:

    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)

Не могли бы вы помочь мне, как реализовать их и получить то, что он хочет, и как & будет работать над этим методом добавления, а также новым и в основном функционале.

1 Ответ

0 голосов
/ 08 января 2019

Вы не совсем понимаете, в чем заключается ваша настоящая проблема - ndash; поэтому я предполагаю, что у вас возникли проблемы с получением желаемого результата для всей книги контактов ...

Во-первых, некоторые другие проблемы:

Нет смысла позволять contact_book наследовать от entry - книга содержит записей, она не одна. Кажется, что вы унаследовали только возможность переопределить функцию displaying, чтобы использовать уже определенный класс operator<< для entry. Но это плохая причина для наследования. Вместо этого вы должны предоставить отдельную перегрузку operator<< для вашей контактной книги:

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

Затем привыкните повторно использовать код там, где это необходимо; Я предполагаю, что getContents должен выводить ту же строку, которая будет записана в std::cout через operator<<. Хорошо, тогда давайте получим прибыль от:

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<< вместо функции 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}

Лично я считаю, что пользовательский интерфейс в целом имеет некоторые веские аргументы, хотя способ, которым он был реализован в 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, тем не менее, чтобы извлечь выгоду из преимуществ), но что бы вы ни решили - пожалуйста, сделайте это последовательно по всему файлу / проекту!

...