Как получить доступ к защищенным структурным переменным из других структур - PullRequest
0 голосов
/ 28 сентября 2018

Итак, я пишу программу на C ++, которая будет имитировать каталог библиотечных карточек.Я определил struct для карточки и всю информацию на каждой карточке, а также рабочие vector и iterator для доступа / печати всех переменных на указанной карточке с использованием глобальной функции void.

Теперь я хочу переместить эту пустую функцию во вновь определенный struct Каталог, который обрабатывает все методы для работы с библиотечными карточками, такие как insert / push_back, search или remove / erase / pop_back.Я также хочу, чтобы мои переменные в карточке были protected, поскольку мне постоянно говорят, что хорошая практика кодирования - иметь переменные вашего класса / структуры равными private (я сделал protected для других унаследованных классов).

//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;

struct Card
{
public: 
    Card(string title, string name)
    {
        this->title = title;
        this->name = name;
    }
//protected:
    string title = "Unknown";
    string name = "Unknown";
};

vector<Card> test;
vector<Card>::iterator it;

void showCard(vector<Card> test)
{
    for (it = test.begin(); it != test.end(); it++)
    {
        if (it->title != "Unknown")
        {
            printf("%s\n", it->title.c_str());
            printf("%s\n", it->name.c_str());
        }
    }
}

int main()
{
    Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
    Card book2 = { "USA and the Middle East since World War 2 / 
    T.G. Fraser.", "T.G. Fraser"};
    Card book3 = { "My Horse and wally", "Jason Weber" };

    test.push_back(book1);
    test.push_back(book2);
    test.push_back(book3);

    showCard(test);

    getchar();
    return 0;
}

Полагаю, мой вопрос в том, как я могу вызвать структуру Catalog из main, чтобы затем получить доступ к защищенным переменным в Card для печати защищенных переменных?

Не может быть так просто, как перечислитьfriend struct Card в каталоге, не так ли?

Редактировать: я поиграл и обнаружил, что friend struct Catalog в разделе Card удалось избавиться от ошибок в функции void для защищенных переменных, к которым она пыталась получить доступ.Я все еще работаю над тем, чтобы основной каталог проходил через каталог, хотя у меня все объекты в main определены как Card.

Полагаю, я мог бы попробовать setCard(), вызванный в main, определенный в Catalog, где ониспользует вектор для ссылки на защищенные переменные.

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Есть несколько способов сделать это, и правильный путь зависит от контекста.Вот несколько возможных решений, от самых простых / хакерских до самых многословных / самых сложных (не исчерпывающий список):


1.Просто сделайте все публично

...

struct Card{
    public: 
    Card(string title, string name){
        this->title = title;
        this->name = name;
    }
    string title = "Unknown";
    string name = "Unknown";
};

...

void showCard(vector<Card> test){
    for (it = test.begin(); it != test.end(); it++){
            if (it->title != "Unknown"){
                    printf("%s\n", it->title.c_str());
                    printf("%s\n", it->name.c_str());
                }
        }
}

Хотя это решает проблему, это не хорошее решение.Если вы когда-нибудь захотите изменить имя члена title на main_title, вам будет очень больно, потому что вам придется каждый раз редактировать title, и это может быстро запутаться.


2.Сделать void showCard(vector<Card> test) другом из структуры Card

Если void showCard(vector<Card> test) является другом Card, то он будет иметь доступ ко всем защищенным и закрытым членам Card как будто они были публичными.Это хорошее решение, потому что только void showCard(vector<Card> test) будет иметь доступ к этим защищенным элементам.

Поскольку вы можете быть только другом ранее объявленных функций, вам нужно будет объявить функцию void showCard(vector<Card> test) перед объявлениемиз Card.

Однако, поскольку void showCard(vector<Card> test) принимает аргумент vector<Card>, класс Card необходимо объявить в прямом порядке перед объявлением функции в прямом направлении.

...
struct Card;
void showCard(vector<Card> test);

struct Card{
    public: 
    friend void showCard(vector<Card> test);
    Card(string title, string name){
        this->title = title;
        this->name = name;
    }
    protected:
    string title = "Unknown";
    string name = "Unknown";
};

...
void showCard(vector<Card> test){
    for (it = test.begin(); it != test.end(); it++){
            if (it->title != "Unknown"){
                    printf("%s\n", it->title.c_str());
                    printf("%s\n", it->name.c_str());
                }
        }
}

3.Создать get уровней и set уровней для Card

Это одна из канонических реализаций .Каждый раз, когда вы делаете члена закрытым / защищенным, вы предоставляете для него методы get_member и set_member.

Таким образом, каждый может получить доступ к члену, однако они могут получить к нему доступ, только если они его используют.методы .Вы даже можете создавать методы получения / установки для несуществующих членов (т.е. вы вычисляете их, когда они вам нужны).

Поскольку код говорит больше, чем слова, вот реализация:

...

struct Card{
    protected:
    string title = "Unknown";
    string name = "Unknown";

    public: 
    Card(string title, string name){
        this->title = title;
        this->name = name;
    }

    string get_title(){
        return this->title;
    }
    void set_title(string new_title){
        this->title = new_title;
    }
    string get_name(){
        return this->name;
    }
    void set_name(string new_name){
        this->name = new_name;
    }
};

...

void showCard(vector<Card> test){
    for (it = test.begin(); it != test.end(); it++){
            if (it->get_title() != "Unknown"){
                    printf("%s\n", it->get_title().c_str());
                    printf("%s\n", it->get_name().c_str());
                }
        }
}

Если вы когда-нибудь захотите изменить имя участника title на main_title, вам нужно будет только отредактировать get_title и set_title, и весь ваш код продолжит работать так, как если бы вы его не менялисовсем.Вы даже можете удалить этот элемент или сделать что-нибудь еще (например, извлечь его из базы данных), потому что единственное место, где его существование и имя имеют значение, находится внутри get_title и set_title.Без геттеров и сеттеров вам нужно было бы редактировать каждый экземпляр title, чтобы сделать это.

Геттеры и сеттеры также являются прекрасными местами для улучшения const правильности вашего кода., делая его более надежным и эффективным.Пара get / set с правильной константой выглядела бы примерно так:

const string& get_title() const {
    return this->title;
}

void set_title(const string& new_title){
    this->title = new_title;
}

А пара для несуществующего члена выглядела бы так:

#include <string>
#include <algorithm>
#include <iterator>

string get_title_and_name(){
    // Concatenates the title and name
    return this->title + " / " + this->name;
}

void set_title_and_name(string new_string){
    // Splits the string between a title and a name
    std::size_t split_point = 0;
    split_point = new_string.find('/');
    this->title = new_string.substr(0, split_point);
    // We don't want to include the char '/' of
    // the new_string in this->name, so use
    // (split_point + 1) instead of split_point
    this->name = new_string.substr(split_point + 1, new_string.size() - (split_point + 1));
}

Хотя это решение можетбыть более многословным, чем другие, он также более гибок.


Предлагаемое решение

Мы можем изменить решение 3, создав новую структуру Catalog и поместив в нее void showCard(vector<Card> test),Это не обычное решение, предоставьте ему возможность избавиться от некоторых глобальных переменных (глобальные переменные почти всегда являются злыми) и скрыть тот факт, что мы используем vector<Card> для сохранения Card с (мы могли быиспользуйте хэш-карту вместо вектора, и это также сработает, поэтому другому коду не нужно знать, какой из них мы выбрали).

//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;


// As in solution 3
struct Card {
protected:
    string title = "Unknown";
    string name = "Unknown";
public: 
    Card(string title, string name){
        this->title = title;
        this->name = name;
    }

    // Right now we only need getters,
    // but we could have setters as well
    // (the names are in camelCase to follow
    //  showCard() naming convention)
    string getTitle(){
        return this->title;
    }
    string getName(){
        return this->name;
    }
};


struct Catalog {
    protected:
    // This one was a global variable previously
    // Also we don't specify a default value
    // for it here, we will do that in the constructor
    vector<Card> test;

    public:
    Catalog(){
        // The start value of test will be a empty vector
        this->test = vector<Card>();
    }

    // We moved void showCard(vector<Card> test) to here
    void showCard(){
        // This is a local variable now
        vector<Card>::iterator it;

        // For loop as in solution 3
        for (it = this->test.begin(); it != this->test.end(); it++){
                if (it->getTitle() != "Unknown"){
                        printf("%s\n", it->getTitle().c_str());
                        printf("%s\n", it->getName().c_str());
                    }
            }
    }

    // A new method for adding cards,
    // because external code shouldn't care
    // about how we add or remove card or even
    // if we store cards in this machine or in a web server
    void addCard(Card card){
        this->test.push_back(card);
    }
};

int main()
{
    Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
    Card book2 = { "USA and the Middle East since World War 2 / T.G. Fraser.", "T.G. Fraser"};
    Card book3 = { "My Horse and wally", "Jason Weber" };
    Catalog catalog;


    catalog.addCard(book1);
    catalog.addCard(book2);
    catalog.addCard(book3);

    // We could even do something like
    // catalog.addCard({ "My Horse and wally", "Jason Weber" });
    // thankfully to the new addCard method.
    // We wouldn't even need to declare book1, book2 and book3
    // if we used it that way

    catalog.showCard();

    getchar();
    return 0;
}

После того, как вы закончите писать свою программу, вы можетебыть заинтересованным в том, чтобы показать его в Code Review , чтобы получить представление о том, как другие люди будут подходить к тому же коду, и узнать, как люди с большим опытом или с другим опытом написали бы такой код.

0 голосов
/ 28 сентября 2018

@ obidyne, добро пожаловать в StackOverflow.Если ваша цель состоит в том, чтобы сохранить элементы защищенными, но при этом иметь возможность показывать их (в виде отформатированной строки), вы можете реализовать открытый метод showCard, переименовать другую функцию showCards и вызвать открытый метод для каждого объектавектор.

Просто пример (используя ваш собственный код):

//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;

struct Card
{
public: 
    Card(string title, string name)
    {
        this->title = title;
        this->name = name;
    }
    void showCard()
    {
        if (this->title != "Unknown")
        {
            printf("%s\n", this->title.c_str());
            printf("%s\n", this->name.c_str());
        }
    }
protected:
    string title = "Unknown";
    string name = "Unknown";
};

vector<Card> test;
vector<Card>::iterator it;

void showCards(vector<Card> test)
{
    for (it = test.begin(); it != test.end(); it++)
    {
        it->showCard();
    }
}

int main()
{
    Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
    Card book2 = { "USA and the Middle East since World War 2 / 
    T.G. Fraser.", "T.G. Fraser"};
    Card book3 = { "My Horse and wally", "Jason Weber" };

    test.push_back(book1);
    test.push_back(book2);
    test.push_back(book3);

    showCards(test);

    getchar();
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...