Привет и добро пожаловать в C ++.Многие скажут, что C ++ - плохой язык для начинающих.Ну, я давно начал с C ++ и до сих пор учусь.Я на 100% самоучка, и у меня не было преимуществ, которые люди имеют сегодня, с количеством информации, доступной через Интернет, а также с современными форматами, которые доступны.Когда я впервые начал изучать C ++, интернет находился в стадии бурного развития;но большинство веб-сайтов в то время представляли собой почти чистый текст, и даже простые картинки или графики требовали времени для загрузки на экран, потому что это было в эпоху Dial Up
.Сегодня люди, которые начинают свою деятельность, имеют преимущества как этого сайта, так и других похожих сайтов и даже видео на YouTube.Тем не менее, я все еще люблю помогать, где могу, потому что это не только помогает вам, но и помогает мне улучшить то, что я уже изучил.C ++ развивался на протяжении многих лет, поэтому я покажу вам небольшое приложение, которое, как мне кажется, имитирует поведение того, что вы описали и пытаетесь сделать.Некоторые из этих методов немного продвинуты, особенно для начинающих, однако я думаю, что это хорошо, если кто-то новичок изучает эти концепции рано.
Storage Types & Lifetime
- В C++
есть в основном 4 основных типа хранения: Automatic
, Dynamic
, Static
и Thread
.Я в основном сосредотачиваюсь на первых 3.
Automatic:
У него есть срок действия области, в которой он объявлен, и он будет автоматически уничтожен, как только эта область выйдет из закрывающей скобки }
Dynamic:
Память, представленная указателями, но объявленная с new
и должна иметь соответствующие delete
или массивы new[]
и delete[]
соответственно.Они могут жить дольше, чем область, в которой они объявлены. Они будут жить, пока не будет вызвано соответствующее удаление.Если соответствующее удаление не происходит, это приводит к утечке памяти, неверным указателям, висящим указателям и ссылкам и неопределенному поведению.Особое внимание необходимо уделять при использовании raw-pointers
;Рекомендуется использовать containers
или smart pointers
. Static:
Они обычно находятся в пространстве имен global
и или global filespace
.Если static
объявлено в main.cpp
, у него будет время жизни приложения и область действия всей программы.Если они объявлены в других файлах cpp
, они будут иметь область действия этого файла, если только они не объявлены в каком-либо файле header
, тогда они будут иметь область действия того, что другой модуль перевода включает в себя этот заголовок.Они похожи на Automatic
в том смысле, что они будут автоматически уничтожены, но отличаются тем, что они инициализируются только один раз, сохраняют свое состояние, и вы можете иметь только один их экземпляр. - Для демонстрации различных типов классификаций хранения вы можете увидеть мой предыдущий ответ на этот вопрос Q / A .
Classes and Inheritance:
- (IPolymorphism
).
Classes:
Classes
и Structs
- определяемые пользователем типы данных. - Разница между двумяэто доступ по умолчанию
- По умолчанию:
Structs
имеет Public Members
& Classes
имеет Private Members
- Членами
Class
или Struct
могут быть любые встроенные типы, указатели на типы, другой определенный пользователем тип данных, а также методы или функции. Member Variables
может быть любого Storage
Type
: Automatic
, Dynamic
, Static
и Thread
однако функции-члены обычно Automatic
, но могут быть Static
.Если у вас есть member variable
, он должен быть initialized
вне Class's
Declaration
, чтобы разрешить его symbols
. - У них есть
Constructors
и Destructors
по умолчанию, но одинМожно создавать свои собственные Constructor
или Destructor
.Обычно вы видите, как люди упоминают их по коротким именам: ctor
& dtor
соответственно. - Один класс может наследовать от другого.
Inheritance:
- У вас есть
Base
или Super
Classes
, и у вас есть Derived
или Child
Classes
- Когда
Inheriting
из класса Base
, если ctor
класса Base
имеет Public
, это означает, что вы можете создать объект как Base
& Derived
Classes
.Иногда вам нужен такой дизайн, но иногда вы этого не делаете. - Если вы не хотите, чтобы пользователь мог создать экземпляр
Base
Class
, но можетечтобы создать экземпляр Derived Class
, все, что вам нужно сделать, это убедиться, что 1 st его ctor
равно Protected
.Если вы объявите его Private
, то даже ваш Derived
Classes
не сможет получить к нему доступ, и поэтому они не могут быть объявлены.* 2nd Вы хотите убедиться, что ваши Destructors
- dtors
равны Virtual
;в противном случае у вас возникнут проблемы с порядком уничтожения классов. - A
Base
Class
может иметь Member Functions
, что означает Virtual
, что означает, что все Derived
Classes
должны Implement
что Function
. - A
Base
Class
может иметь Member Functions
, равные Purely Virtual
, что аналогично приведенному выше, но также мешает любому объявить экземпляр этого Base
Class
, посколькуэто делает Base
Class
Abstract
, что означает, что это идея для интерфейса. - Пример
Virtual
Function
: virtual update();
- Пример
Purely Virtual
: `virtual update () = 0;
Containers
- (я буду использовать std::container
, но я не буду привлекать Algorithms
)
- Существует множество видов контейнеров для разных нужд;Эти контейнеры помогают сделать вашу программу простой, легкой в управлении и отладке, удобной для пользователя и предотвратить многие будущие головные боли вместо использования
basic C Arrays
. - Существует несколько различных типов, и каждый из них имеет свое собственное назначение исвойства, которые я не буду рассматривать все их детали, так как вы можете найти множество информации в сети, касающейся их, но я буду помечать и группировать их по аналогичным свойствам: группировки
Sequence Containers
, Associative Containers
, & Unordered Containers
и все они принадлежат std::
namespace
.Основное различие между группировками: Sequence Containers
похоже на arrays
и linked lists
, Associative Containers
равно binary trees
и Unordered Containers
похоже на binary trees
за исключением того, что они не в порядке, они считаютсяhash tables
. Sequence:
vector
, dequeue
, queue
, list
, forward_list
, array
Associative:
set
, multiset
, map
, multimap
Unordered:
unordered_set
, unordered_multiset
, unordered_map
, unordered_multimap`
- Что делает эти контейнеры мощными, так это способностьлибо быстро пройти их, либо быстро вставить и найти в зависимости от того, какой контейнер вы используете.Еще одна полезная функция помогает в процессе управления памятью.Наконец, есть многочисленные
algorithms
и iterators
, которые могут работать с ними. - Для получения дополнительной информации о
stl
я советую посмотреть эту серию youtube по Bo Qian
Smart Pointers
Существует несколько различных типов, но наиболее важными являются std::shared_ptr<T>
и std::unique_ptr<T>
.Основное различие между ними состоит в том, что shared
имеет reference count
от того, сколько объектов имеет к нему доступ;это означает, что он имеет интерфейс открытого типа и может использовать семантику копирования;unique
, с другой стороны, имеет только 1
owner
за раз, он может передать право собственности, но как только он у первоначального владельца, к нему больше нет доступа.Вы не можете скопировать unique
, но вы можете переместить их.Эти интеллектуальные указатели помогают с использованием dynamic memory
и управления жизненными объектами для предотвращения утечек памяти, недействительных указателей, висячих указателей и ссылок, неопределенного поведения и т. Д. Они помогают минимизировать использование new
& delete
и new[]
& delete[]
соответственно.
Теперь, когда у вас есть понимание некоторых концепций, которые вы будете встречать довольно часто в C++
программах;Сейчас я покажу вам простое приложение, которое я написал на основе того, что, как я понял, вы пытались сделать:
#include <array>
#include <exception>
#include <memory>
#include <string>
#include <iostream>
class Person {
protected:
std::string name_;
int tasksCompleted_;
public:
const std::string& whoIs() const { return name_; }
int numberTasksCompleted() const { return tasksCompleted_; }
virtual void performTask() = 0;
protected:
explicit Person(const std::string& name) : name_{ std::move(name) } {}
virtual ~Person() = default;
};
class Bodine final : public Person {
private:
static int currentTask;
public:
explicit Bodine(const std::string& name = std::string("Bodine")) : Person(name) {}
virtual ~Bodine() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Bodine::currentTask = 0;
class Finn final : public Person {
private:
static int currentTask;
public:
explicit Finn(const std::string& name = std::string("Finn")) : Person(name) {}
virtual ~Finn() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Finn::currentTask = 0;
class Tycho final : public Person {
private:
static int currentTask;
public:
explicit Tycho(const std::string& name = std::string("Tycho")) : Person(name) {}
virtual ~Tycho() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Tycho::currentTask = 0;
int main() {
try {
std::array<std::shared_ptr<Person>, 3> people{
std::shared_ptr<Person>(new Bodine()),
std::shared_ptr<Person>(new Finn()),
std::shared_ptr<Person>(new Tycho())
};
// For each person in array
const int MAX_TASKS = 7;
int currentPerson = 0;
for (auto& p : people) {
std::cout << p->whoIs() << " has performed task #:\n";
while (p->numberTasksCompleted() < 7) {
p->performTask();
std::cout << p->numberTasksCompleted() << '\n';
if (p->numberTasksCompleted() == MAX_TASKS) {
currentPerson++;
if (currentPerson <= (people.size() - 1) ) {
std::cout << "It's your turn " << people[currentPerson]->whoIs() << " to do some tasks.\n";
}
break;
}
}
}
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-Выход-
Bodine has performed task #:
1
2
3
4
5
6
7
Finn has performed task #:
1
2
3
4
5
6
7
Tycho has performed task #:
1
2
3
4
5
6
7
Ссылка на вышеуказанную программу.
Я знаю, что это совсем немного для чтения;но, пожалуйста, внимательно посмотрите на приложение, чтобы попробовать и посмотреть, что оно делает.Вы можете скопировать и вставить это прямо в вашу собственную IDE и попытаться собрать и запустить его, чтобы увидеть его в действии.Надеюсь, это даст вам понимание того, что такое C++
.
-Edit-
О коде выше: он немного сложнее, чем он должен быть на самом деле.Во-первых, вы бы не увидели личность одного человека, как его собственный класс.Я только сделал это, чтобы продемонстрировать, что такое inheritance
.Я решил использовать std::array
вместо std::vector
в основном из-за того, что у вас было 3
конкретных людей из вашего предложения.Если у вас есть неизвестное количество людей во время компиляции, тогда std::vector
будет правильным контейнером для использования.Вам действительно не нужно было бы использовать наследование.Также, если вы посмотрите на 3
производное classes
, там много кода duplicate
.Я также использовал shared_ptrs
, чтобы показать, как их можно использовать, но они не нужны в вашей конкретной ситуации.Я также использовал static member variables
в Derived
Classes
, чтобы показать static storage
.
Вот упрощенная версия приведенного выше кода, имитирующая ваше поведение.Обе программы выполняют одну и ту же задачу аналогично, единственными существенными различиями являются: * Использование inheritance
с purely virtual methods
, static member storage
, container type
и dynamic memory
с использованием shared_ptr
.
#include <exception>
#include <iostream>
#include <string>
#include <vector>
class Person {
private:
std::string name_;
int tasksCompleted_;
public:
explicit Person(const std::string& name) : name_(name), tasksCompleted_(0) {}
const std::string& whoIs() const { return name_; }
int numberTasksCompleted() const { return tasksCompleted_; }
void performTask() { tasksCompleted_++; }
};
int main() {
try {
std::vector<Person> people{
Person( "Bodine" ),
Person( "Finn" ),
Person( "Tycho" )
};
// For each person in array
const int MAX_TASKS = 7; // Don't like magic numbers!
int currentPerson = 0; // Needed variable
for (auto& p : people) {
std::cout << p.whoIs() << " has performed task #:\n";
while (p.numberTasksCompleted() < MAX_TASKS) {
p.performTask();
std::cout << p.numberTasksCompleted() << '\n';
if (p.numberTasksCompleted() == MAX_TASKS) {
currentPerson++;
if (currentPerson <= (people.size() - 1) ) {
std::cout << "It's your turn " << people[currentPerson].whoIs() << " to do some tasks.\n";
}
break;
}
}
}
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Вот ссылка для этой версии программы!