private
private
Члены, указатели или другие данные - это данные, которые объект либо не хочет испортить кому-либо еще, либо хочет знать, с кем-то с ними столкнулся.
Способы сета и геттера позволяют получить доступ к члену private
, но контролируемым образом. Например, если у вас есть личное целое число, которое ни при каких обстоятельствах не может быть больше десяти, вы можете иметь код в сеттере, который проверяет, что вызывающий объект пытается вывести значение из диапазона и отклонить запрос.
bool setX(int newX)
{
if (newX > 10)
{
return false;
}
else
{
X = newX;
return true;
}
}
Теперь программа не может иметь каких-либо неприятных сюрпризов с X == 11, создающего вне диапазона доступа или любой другой.
Это самооборона для объектов. Они сохраняют контроль над тем, кто устанавливает свои данные для чего и может поддерживать согласованность. Скажем, у вас более сложный случай, когда вы не можете пробовать A/D более 10000 выборок в секунду с включенным фильтром FIR без голодания процессора и блокировки системы. Упс. Если единственный способ установить состояние фильтра или частоту дискретизации - через сеттеры в объекте диспетчера A/D, объект может протестировать и отклонить и предотвратить катастрофу (и, возможно, оставить сообщение с хорошим сообщением в маске, указывающее на плохого актера).
Подумайте очень сложно, прежде чем внедрять геттер, который возвращает ссылку на константу или указатель. Как только вызывающий абонент имеет либо, они могут делать все, что захотят, с возвращенными данными.
Правило большого пальца по умолчанию равно паранойи: не допускайте доступа к каким-либо данным без уважительной причины, а затем предпочитайте контролируемый доступ через сеттеры и геттеры.
О специфике вашей проблемы.
Геттеры и сеттеры для узла связи часто являются ставкой присоски. Узел, скорее всего, не может определить для себя, если ссылка действительна. Только менеджер списка может. Это тот случай, когда сам объект слишком незначителен, чтобы знать, что безопасно, поэтому вам нужно открыть внутренности для другого объекта, который знает больше. friend
полезен здесь, хотя часто бывает лучше сделать ссылки узла public
и никогда не разрешать диспетчеру списка давать узел клиенту.
Коэффициенты хорошие, клиент не должен знать абсолютно ничего о том, как список работает в любом случае. Прочтите на сцепление.
Итак, узел должен быть совершенно глупым. Это означает, что вам нужно иметь класс ListManager
, чтобы (duh) управлять списком и защищать node
от плохо действующих актеров.
ListManager
содержит ваш head
, tail
, root
или что-то вместе с append
и remove
, print
и другими методами управления списком. Ни при каких обстоятельствах никакая из этих функций не вызывает node
вызывающему абоненту, хотя они могут возвращать дескриптор или итератор, который можно использовать для ссылки на узел, не предоставляя вызывающему устройству инструмент для повреждения списка. Итераторы - это тема, достойная их собственного вопроса и, вероятно, уже довольно много.
Немного кода для объяснения приведенного выше в порядке. Обратите внимание, что я отметил, но не исправил логические проблемы, которые я нашел. Может быть больше, поскольку этот компилятор (с включенным C++ 11), но я его не запускал.
class ListManager
{
private:
class node
{
public:
node *left_link = nullptr; // recommendation: immediately set or NULL all
// pointers unless you have a well documented
// reason not to and profiling to back it up.
// The time you save can be enormous.
char anything;
node *right_link = nullptr;
};
node *head = nullptr;
node *tail = nullptr;
public:
void append(char c) // head and tail not required ListManager members
{
/* removed because the append function should append and only append.
* If you want to read data from the user, call a read function first
* and pass it read character in to append
* Do one thing and do it well. Every time you add behaviours to a
* function, you make it harder to debug. For example, what happens to
* the linked list if you fail to read a character? That shouldn't be
* append's problem.
char c;
std::cout << "Please enter a single character: ";
std::cin >> c;
*/
node *current = new node();
current->anything = c;
//std::cout << current->anything << std::endl; removed for same reason as above.
// think on this: how can head and tail NOT both be NULL at the same time?
// if you find a way, you have a bug that needs fixing.
if (head == nullptr && tail == nullptr)
{
// If head is NULL, it has no right_link to assign. This will fail horribly.
head->right_link = current;
tail->left_link = current;
current->left_link = head;
current->right_link = tail;
/* Consider instead
head = current;
tail = current;
*/
}
else
{
tail->right_link = current;
current->left_link = tail;
tail = current;
tail->right_link = nullptr; // don't need to do this. node constructor
// ensures current->right_link is NULL
}
}
// print function
void print() // no parameters required. head is ListManager member
{
node* temp;
temp = head;
// Again, if head is NULL, temp will be NULL and there will be no right_link
// consider instead
// while (temp != nullptr)
while (temp->right_link != nullptr)
{
std::cout << temp->anything << std::endl;
temp = temp->right_link;
}
}
};
Обратите внимание, как node
встроен прямо в ListManager
и private
. Теперь только ListManager
имеет доступ к node
и имеет полный доступ.
ListManager
также нуждается в деструкторе для обработки delete
всех добавленных узлов new
. Ему также нужен конструктор копирования и оператор присваивания, чтобы сделать его правилом три. «What is The Rule of Three?» спросите вы? Это очень важно. Прочтите ссылку, чтобы сохранить большую часть будущей отладки. Несоблюдение правила из трех приводит к непропорциональному числу вопросов на C++, связанных с переполнением стека, и нет смысла в дальнейшем накачивать эту статистику.
Использование:
int main()
{
ListManager list;
char choice;
std::cout << "Please choose one menu option at a time:\n" << "1 = Append\n"
<< "2 = Print list\n" << "3 = Exit\n\n";
do
{
std::cout << "Menu option(1-3): ";
std::cin >> choice;
switch (choice)
{
case '1':
list.append('a'); // add to the end of list.
break;
case '2':
list.print(); // print list
break;
case '3':
std::cout << "end program\n\n";
break;
default:
std::cout << "try again\n";
break;
}
} while (choice != '3');
return 0;
}
Я сомневаюсь, что ваша проблема не имеет ничего общего с переменными является частным. – immibis
*** код запускается, но он ничего не делает, когда я использую функцию печати. *** Мой совет вам научиться использовать ваш отладчик. Один шаг через код, смотрящий на переменные .. – drescherjm
Вы либо неправильно поняли концепцию связанных списков, либо указатели C++. Если у вас нет элементов, тогда вы должны создать новый узел, который станет как головой, так и хвостом, а затем установите для него правые и левые соседи в значение «nullptr».Просмотрите инструкцию 'if' внутри' append', она включает ** неопределенное поведение **. – K117