所以我正在编写一个 C++ 程序来模拟图书证目录。我为卡片和每张卡片上的所有信息定义了一个 struct
,以及一个工作 vector
和 iterator
来访问/打印所有变量使用全局无效函数指定的卡。
现在,我想在一个新定义的 struct
中移动那个 void 函数,Catalog 处理所有处理借书证的方法,比如 insert
/ push_back
、search
或remove
/erase
/pop_back
。我还希望 card 下的变量受到 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 下的 protected 变量以便打印 protected 变量?
不能像在目录中列出 friend struct Card
那样简单吗?
编辑:我试了一下,发现 Card 下的 friend struct Catalog
能够消除它尝试访问的 protected 变量的 void 函数中的错误。尽管我将 main
中的所有对象都定义为 Card
,但我仍在努力让 main pass through catalog。
我想我可以尝试在 main 中调用 setCard()
,在 Catalog 中定义,它使用 vector 来引用 protected 变量。
最佳答案
有多种方法可以做到,正确的方法取决于上下文。以下是一些可能的解决方案,从最简单/最难的到最冗长/最难的(不是详尽的 list ):
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());
}
}
}
虽然这确实解决了问题,但这不是一个好的解决方案。如果您想更改成员(member)的姓名 title
至 main_title
你这样做会很痛苦,因为你必须编辑每一次出现的title
。这很快就会变得一团糟。
2。制作void showCard(vector<Card> test)
一个friend结构 Card
如果void showCard(vector<Card> test)
是Card
的 friend 然后它将可以访问 Card
的所有 protected 和私有(private)成员好像他们是公开的。这是一个很好的解决方案,因为只有 void showCard(vector<Card> test)
将有权访问这些 protected 成员。
因为您只能成为先前声明函数的友元,所以您需要转发声明函数 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
条款
This is one of the canonical implementations .每次将成员设为私有(private)/ protected 时,您都会提供 get_member
和一个 set_member
方法。
这样每个人都可以访问该成员,但是,他们只有在使用这些方法时才能访问它。您甚至可以为不存在的成员创建 getter/setter(即您在需要时计算它们)。
既然代码胜于 Eloquent ,下面是一个实现:
...
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());
}
}
}
如果您想更改成员(member)的姓名 title
至 main_title
, 你只需要编辑 get_title
和 set_title
并且您的所有代码将继续工作,就好像您根本没有更改它一样。您甚至可以删除该成员或执行任何其他操作(例如从数据库中获取它),因为它存在且名称重要的唯一位置是在 get_title
中。和 set_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
来修改解决方案 3并放置 void showCard(vector<Card> test)
在里面。这不是一个通常的解决方案,它打开了我们摆脱一些全局变量(全局变量几乎总是邪恶的)并隐藏我们正在使用 vector<Card>
的事实的可能性。保持Card
s(我们可以使用 hashmap 而不是 vector,这样也可以,这样其他代码就不需要知道我们从两者中选择了哪一个)。
//#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 上展示它以获得关于其他人如何处理相同代码的见解,并了解更有经验或具有不同背景的人如何编写此类代码。
关于c++ - 如何从其他结构访问 protected 结构变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52546965/