因此,我正在编写一个模仿图书馆卡目录的C ++程序。我为卡和每个卡上的所有信息定义了一个struct
,以及一个有效的vector
和iterator
来使用全局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
,它使用向量来引用受保护的变量。
答案 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到最冗长/最困难(不是详尽的清单):
...
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
的每一个出现,并且可以很快变得混乱。
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());
}
}
}
get
set
ters和Card
ters
friend。每次将成员设为私有/受保护成员时,都会为其提供get_member
和set_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_title
和set_title
,所有代码都将保留就像您根本没有更改一样。您甚至可以删除该成员或执行其他任何操作(例如从数据库中获取它),因为存在该名称和名称的唯一位置是在get_title
和set_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上展示它,以便获得有关其他人将如何处理相同代码的见解,并了解经验丰富或背景不同的人如何编写程序的见解。这样的代码。