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