如何从其他结构访问受保护的结构变量

时间:2018-09-28 01:10:13

标签: c++ vector struct protected

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

现在,我想在一个新定义的struct中移动该void函数,该Catalog处理所有处理图书馆卡的方法,例如insert / push_back,{{1 }}或search / remove / erase。我还希望将卡片下的变量设置为pop_back,因为我经常被告知将您的类/结构变量设置为protected是很好的编码习惯(我对其他类做了private继承)。

protected

我想我的问题是,我怎样才能从main调用Catalog struct然后访问Card下的受保护变量以打印受保护变量?

会不会像在目录中列出//#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; } 一样简单?

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

我想我可以尝试在目录中定义的main中调用Card,它使用向量来引用受保护的变量。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

有多种方法可以执行此操作,正确的方法取决于上下文。以下是一些可能的解决方案,从最简单/最hackest到最冗长/最困难(不是详尽的清单):


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的{​​{3}}

如果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 ters和Card ters

friend。每次将成员设为私有/受保护成员时,都会为其提供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的每一个出现。

字母和设置者也是改善代码This is one of the canonical implementations的绝妙地方,使其更加健壮和高效。 const正确的获取/设置对看起来像这样:

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<Card> test)放入其中来修改解决方案3。这不是通常的解决方案,它使我们有可能摆脱一些全局变量(全局变量几乎总是有害的),并掩盖了我们使用vector<Card>来保留Card的事实s(我们可以使用哈希图代替向量,并且也可以使用,因此其他代码不需要知道我们选择了两者中的哪一个)。

//#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;
}

在结束编写程序之后,您可能有兴趣在const correctness上展示它,以便获得有关其他人将如何处理相同代码的见解,并了解经验丰富或背景不同的人如何编写程序的见解。这样的代码。