【问题标题】:How to access protected structure variables from other structures如何从其他结构访问受保护的结构变量
【发布时间】:2019-03-03 22:58:49
【问题描述】:

所以我正在编写一个模仿图书馆卡片目录的 C++ 程序。我为卡和每张卡上的所有信息定义了一个struct,以及一个工作vectoriterator 使用全局void 函数访问/打印指定卡上的所有变量。

现在,我想在新定义的 struct 中移动该 void 函数,该目录处理所有处理借书证的方法,例如 insert/push_backsearchremove/@ 987654330@/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;
}

我想我的问题是,如何从 main 调用 Catalog 结构,然后访问 Card 下的受保护变量以打印受保护变量?

不能像在目录中列出friend struct Card 那样简单吗?

编辑:我玩了一下,发现 Card 下的 friend struct Catalog 能够消除它尝试访问的受保护变量的 void 函数中的错误。尽管我将main 中的所有对象都定义为Card,但我仍在努力通过目录进行主要传递。

我想我可以尝试在 main 中调用 setCard(),在 Catalog 中定义它使用向量来引用受保护的变量。

【问题讨论】:

标签: c++ vector struct protected


【解决方案1】:

有多种方法可以做到这一点,正确的方法取决于上下文。以下是一些可能的解决方案,从最简单/最骇人听闻的到最冗长/最难的(不是详尽的列表):


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&lt;Card&gt; test) 成为结构Cardfriend

如果void showCard(vector&lt;Card&gt; test)Card 的朋友,那么它将可以访问Card 的所有受保护和私有成员就好像他们是公开的。这是一个很好的解决方案,因为只有 void showCard(vector&lt;Card&gt; test) 才能访问这些受保护的成员。

因为你只能是之前声明的函数的朋友,你需要在声明Card之前转发声明函数void showCard(vector&lt;Card&gt; test)

但是,因为void showCard(vector&lt;Card&gt; test) 接受vector&lt;Card&gt; 参数,所以类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。为Card 创建getters 和setters

This is one of the canonical implementations。每次将成员设为私有/受保护时,您都会为其提供 get_memberset_member 方法。

这样每个人都可以访问该成员,但是,他们只有在使用这些方法时才能访问它。您甚至可以为不存在的成员创建 getter/setter(即在需要时计算它们)。

由于代码胜于雄辩,这里有一个实现:

...

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_titleset_title,您的所有代码都会像没有更改一样继续工作它根本没有。您甚至可以删除该成员或执行任何其他操作(例如从数据库中获取它),因为它存在和名称的唯一位置是在 get_titleset_title 内部。如果没有 getter 和 setter,您需要编辑 title 的每一次出现才能做到这一点。

Getter 和 setter 也是改进代码 const correctness 的好地方,使其更加健壮和高效。一个 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));
}

虽然此解决方案可能比其他解决方案更冗长,但它也更灵活。


建议的解决方案

我们可以通过创建一个新结构 Catalog 并将 void showCard(vector&lt;Card&gt; test) 放入其中来修改解决方案 3。这不是一个常见的解决方案,让我们有可能摆脱一些全局变量(全局变量几乎总是邪恶的)并隐藏我们使用vector&lt;Card&gt; 来保留Cards 的事实(我们可以使用哈希图而不是向量,这也可以,因此其他代码不需要知道我们在两者中选择了哪一个。

//#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 上展示它,以便了解其他人如何处理相同的代码,并了解更有经验或具有不同背景的人如何编写此类代码.

【讨论】:

    【解决方案2】:

    @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;
    }
    

    【讨论】:

    • 我刚刚可视化了您的代码并意识到,哦,是的,这个人实际上是自己翻牌的,哈哈,我认为由于矢量 test 是全局的,我们可能不需要 showCards 的任何输入参数,会我们?另外,我收到一条错误消息,提示 it-&gt;showCard(); 中的 showCard 不是 struct Card 的成员
    • 我意识到我将 void showCard() 移动到了我的新 struct Catalog。我什至需要一个目录结构类吗?我可以只有一个名为 card 的结构,所有目录都可以简单地是向量,对吗?
    • 好吧,你不应该依赖 Globals。并且“目录”结构可能对实现其他方法很有用,例如showCards。想象一下Card 用于纸牌游戏,创建Deck 是否有意义?我坚信会的。但是,当然,您必须分析您的需求以及如何改进您的类以使您的代码更好,从而使您的生活更轻松。
    • 好主意;可以将这些结构保存在头文件中以保存扑克程序大声笑现在我需要一个工作目录来查找和打印图书馆书籍信息。对于从主函数或公共函数调用 Catalog 结构的成员有什么大神建议吗?
    • 我收到错误断言失败消息,现在程序甚至无法运行。上面写着:can't dereference value-initialized vector iterator
    猜你喜欢
    • 1970-01-01
    • 2010-11-25
    • 2019-04-18
    • 1970-01-01
    • 2010-10-18
    • 2018-09-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    相关资源
    最近更新 更多