mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-25 08:13:29 +03:00
Adds complete translation
This commit is contained in:
parent
83f3d7dc3b
commit
aad2d2b6f2
@ -162,7 +162,7 @@ void foo()
|
||||
|
||||
int main()
|
||||
{
|
||||
// Включает все функци с пространства имен Second в текущую область видиомти.
|
||||
// Включает все функци с пространства имен Second в текущую область видимости.
|
||||
// Обратите внимание, что простой вызов foo() больше не работает,
|
||||
// так как теперь не ясно вызываем ли мы foo с пространства имен Second или
|
||||
// из глобальной области видимости.
|
||||
@ -177,26 +177,26 @@ int main()
|
||||
// Ввод/Вывод
|
||||
///////////////
|
||||
|
||||
// C++ input and output uses streams
|
||||
// cin, cout, and cerr represent stdin, stdout, and stderr.
|
||||
// << is the insertion operator and >> is the extraction operator.
|
||||
// Вводи и вывод в C++ использует потоки
|
||||
// cin, cout и cerr предоставлнеы методами stdin, stdout и stderr.
|
||||
// << - оператор вставки, >> - оператор извлечения.
|
||||
|
||||
#include <iostream> // Include for I/O streams
|
||||
#include <iostream> // Включение файла для работы с потоками Ввода\Вывода.
|
||||
|
||||
using namespace std; // Streams are in the std namespace (standard library)
|
||||
using namespace std; // Потоки доступны в пространстве имен std (стандартная библиотека)
|
||||
|
||||
int main()
|
||||
{
|
||||
int myInt;
|
||||
|
||||
// Prints to stdout (or terminal/screen)
|
||||
// Выводит stdout (или терминал\экран)
|
||||
cout << "Enter your favorite number:\n";
|
||||
// Takes in input
|
||||
// Принимает ввод
|
||||
cin >> myInt;
|
||||
|
||||
// cout can also be formatted
|
||||
// cout может принимать форматирование
|
||||
cout << "Your favorite number is " << myInt << "\n";
|
||||
// prints "Your favorite number is <myInt>"
|
||||
// напечатает "Your favorite number is <myInt>"
|
||||
|
||||
cerr << "Used for error messages";
|
||||
}
|
||||
@ -205,35 +205,35 @@ int main()
|
||||
// Строки
|
||||
//////////
|
||||
|
||||
// Strings in C++ are objects and have many member functions
|
||||
// Строки в C++ являются объектами и имеют много функций-членов.
|
||||
#include <string>
|
||||
|
||||
using namespace std; // Strings are also in the namespace std (standard library)
|
||||
using namespace std; // Строки также доступны в пространстве имен std (стандартная библиотек)
|
||||
|
||||
string myString = "Hello";
|
||||
string myOtherString = " World";
|
||||
|
||||
// + is used for concatenation.
|
||||
// + используется для конкатенации строк.
|
||||
cout << myString + myOtherString; // "Hello World"
|
||||
|
||||
cout << myString + " You"; // "Hello You"
|
||||
|
||||
// C++ strings are mutable and have value semantics.
|
||||
// Строки в C++ могут изменяться и имеют семантику значений.
|
||||
myString.append(" Dog");
|
||||
cout << myString; // "Hello Dog"
|
||||
|
||||
|
||||
/////////////
|
||||
// References
|
||||
// Ссылки
|
||||
/////////////
|
||||
|
||||
// In addition to pointers like the ones in C,
|
||||
// C++ has _references_.
|
||||
// These are pointer types that cannot be reassigned once set
|
||||
// and cannot be null.
|
||||
// They also have the same syntax as the variable itself:
|
||||
// No * is needed for dereferencing and
|
||||
// & (address of) is not used for assignment.
|
||||
// В добавок к указателям доступных в C,
|
||||
// C++ имеет _ссылки_.
|
||||
// Это такой тип указателя, который не может быть переназначен после инициализации
|
||||
// и не может иметь значения null.
|
||||
// Ссылки имеют схожий с переменными синтаксис:
|
||||
// * больше не используется для разыменования и
|
||||
// & (адрес) не используется для назначения.
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -241,84 +241,80 @@ string foo = "I am foo";
|
||||
string bar = "I am bar";
|
||||
|
||||
|
||||
string& fooRef = foo; // This creates a reference to foo.
|
||||
fooRef += ". Hi!"; // Modifies foo through the reference
|
||||
cout << fooRef; // Prints "I am foo. Hi!"
|
||||
string& fooRef = foo; // Здесь создается указатель на foo.
|
||||
fooRef += ". Hi!"; // Изменяет foo по ссылке
|
||||
cout << fooRef; // Печатает "I am foo. Hi!"
|
||||
|
||||
// Doesn't reassign "fooRef". This is the same as "foo = bar", and
|
||||
// Не переназначает "fooRef". Это тоже самое как "foo = bar", и
|
||||
// foo == "I am bar"
|
||||
// after this line.
|
||||
cout << &fooRef << endl; //Prints the address of foo
|
||||
// после этой строчки.
|
||||
cout << &fooRef << endl; // Печатает адрес foo
|
||||
fooRef = bar;
|
||||
cout << &fooRef << endl; //Still prints the address of foo
|
||||
cout << fooRef; // Prints "I am bar"
|
||||
cout << &fooRef << endl; // По-прежнему печатает адрес foo
|
||||
cout << fooRef; // Печатает "I am bar"
|
||||
|
||||
//The address of fooRef remains the same, i.e. it is still referring to foo.
|
||||
// Адрес fooRef остается тем же, то есть он по-прежнему ссылается на foo.
|
||||
|
||||
|
||||
const string& barRef = bar; // Create a const reference to bar.
|
||||
// Like C, const values (and pointers and references) cannot be modified.
|
||||
barRef += ". Hi!"; // Error, const references cannot be modified.
|
||||
const string& barRef = bar; // Создает const со ссылкой на bar.
|
||||
// Также как и C, значения const (и указателей и ссылок) не могут быть изменены.
|
||||
barRef += ". Hi!"; // Ошибка, указатель const не может быть изменен.
|
||||
|
||||
// Sidetrack: Before we talk more about references, we must introduce a concept
|
||||
// called a temporary object. Suppose we have the following code:
|
||||
// Обходной путь: Прежде чем мы рассмотрим указатели более детально, нам нужно ознакомится
|
||||
// с концепцией известной как "временный объект". Представьте, что мы имеем следующий код
|
||||
string tempObjectFun() { ... }
|
||||
string retVal = tempObjectFun();
|
||||
|
||||
// What happens in the second line is actually:
|
||||
// - a string object is returned from tempObjectFun
|
||||
// - a new string is constructed with the returned object as argument to the
|
||||
// constructor
|
||||
// - the returned object is destroyed
|
||||
// The returned object is called a temporary object. Temporary objects are
|
||||
// created whenever a function returns an object, and they are destroyed at the
|
||||
// end of the evaluation of the enclosing expression (Well, this is what the
|
||||
// standard says, but compilers are allowed to change this behavior. Look up
|
||||
// "return value optimization" if you're into this kind of details). So in this
|
||||
// code:
|
||||
// Вот что на самом деле происходит во второй строе:
|
||||
// - tempObjectFun возвращает строковый объект
|
||||
// - с возвращаемого объекта создается новая строка в качестве аргумента конструктору
|
||||
// - возвращаемый объект уничтожается
|
||||
// Возвращаемый объект называется временным объектом. Временные объекты создаются
|
||||
// когда функция возвращает объект, и уничтожаются в конце выполнения обрамляющего
|
||||
// выражения (По крайней мере, так это описывает спецификация, хотя компиляторы могут
|
||||
// изменять это поведение. Для более подробной информации смотрите "оптимизация
|
||||
// возвращаемого значения". Таким образом в этом коде:
|
||||
foo(bar(tempObjectFun()))
|
||||
|
||||
// assuming foo and bar exist, the object returned from tempObjectFun is
|
||||
// passed to bar, and it is destroyed before foo is called.
|
||||
// предполагая, что foo и bar существуют, объект возвращаемый tempObjectFun передается
|
||||
// в bar, и уничтожается перед вызовом foo.
|
||||
|
||||
// Now back to references. The exception to the "at the end of the enclosing
|
||||
// expression" rule is if a temporary object is bound to a const reference, in
|
||||
// which case its life gets extended to the current scope:
|
||||
// Возвращаемся к указателям. Исключением для правила "в конце выполнения обрамляющего
|
||||
// выражения" является временный объект привязанный к ссылке const, в этом случае
|
||||
// его жизненный цикл продлевается до текущей области видимости:
|
||||
|
||||
void constReferenceTempObjectFun() {
|
||||
// constRef gets the temporary object, and it is valid until the end of this
|
||||
// function.
|
||||
// constRef получает временный объект, и он действителен до конца этой функции.
|
||||
const string& constRef = tempObjectFun();
|
||||
...
|
||||
}
|
||||
|
||||
// Another kind of reference introduced in C++11 is specifically for temporary
|
||||
// objects. You cannot have a variable of its type, but it takes precedence in
|
||||
// overload resolution:
|
||||
// В C++11 предоставлен еще один тип ссылок специально для временных объектов.
|
||||
// objects. Вы не можете объявить переменную этого типа, но он имеет приоритет в
|
||||
// в резолюции перегрузки:
|
||||
|
||||
void someFun(string& s) { ... } // Regular reference
|
||||
void someFun(string&& s) { ... } // Reference to temporary object
|
||||
void someFun(string& s) { ... } // Обычная ссылка
|
||||
void someFun(string&& s) { ... } // Ссылка на временный объект
|
||||
|
||||
string foo;
|
||||
someFun(foo); // Calls the version with regular reference
|
||||
someFun(tempObjectFun()); // Calls the version with temporary reference
|
||||
someFun(foo); // Выполняет версию с обычной ссылкой
|
||||
someFun(tempObjectFun()); // Выполняет версию с временной ссылкой.
|
||||
|
||||
// For example, you will see these two versions of constructors for
|
||||
// std::basic_string:
|
||||
// Например, существуют следующие две версии конструктора std::basic_string:
|
||||
basic_string(const basic_string& other);
|
||||
basic_string(basic_string&& other);
|
||||
|
||||
// Idea being if we are constructing a new string from a temporary object (which
|
||||
// is going to be destroyed soon anyway), we can have a more efficient
|
||||
// constructor that "salvages" parts of that temporary string. You will see this
|
||||
// concept referred to as "move semantics".
|
||||
// Идея в том, если мы конструируем новую строку из временного объекта (который
|
||||
// так или иначе будет уничтожен), мы можем использовать более эффективный конструктор,
|
||||
// который "спасает" части этой временной строки. Эта концепция была названа
|
||||
// "move semantics".
|
||||
|
||||
/////////////////////
|
||||
// Enums
|
||||
// Перечисления
|
||||
/////////////////////
|
||||
|
||||
// Enums are a way to assign a value to a constant most commonly used for
|
||||
// easier visualization and reading of code
|
||||
// Перечисления - способ объявления констант и установки их значений в основном
|
||||
// использующийся для упрощения чтения кода.
|
||||
enum ECarTypes
|
||||
{
|
||||
Sedan,
|
||||
@ -332,9 +328,9 @@ ECarTypes GetPreferredCarType()
|
||||
return ECarTypes::Hatchback;
|
||||
}
|
||||
|
||||
// As of C++11 there is an easy way to assign a type to the enum which can be
|
||||
// useful in serialization of data and converting enums back-and-forth between
|
||||
// the desired type and their respective constants
|
||||
// На момент выхода C++11 есть простой способ назначения типа перечисления, что
|
||||
// полезно в случае сериализации данных и преобразований между конечным типом и
|
||||
// соответствующими константами.
|
||||
enum ECarTypes : uint8_t
|
||||
{
|
||||
Sedan, // 0
|
||||
@ -345,18 +341,19 @@ enum ECarTypes : uint8_t
|
||||
|
||||
void WriteByteToFile(uint8_t InputValue)
|
||||
{
|
||||
// Serialize the InputValue to a file
|
||||
// Сериализуем InputValue в файл
|
||||
}
|
||||
|
||||
void WritePreferredCarTypeToFile(ECarTypes InputCarType)
|
||||
{
|
||||
// The enum is implicitly converted to a uint8_t due to its declared enum type
|
||||
// Перечисление неявно преобразуется в uint8_t из-за раннее объявленного
|
||||
// типа перечисления.
|
||||
WriteByteToFile(InputCarType);
|
||||
}
|
||||
|
||||
// On the other hand you may not want enums to be accidentally cast to an integer
|
||||
// type or to other enums so it is instead possible to create an enum class which
|
||||
// won't be implicitly converted
|
||||
// С другой стороны, чтобы избежать случайного приведения к целочисленному типу или
|
||||
// другому перечислению, вы можете создать класс перечисления, который не будет
|
||||
// преобразовываться неявно.
|
||||
enum class ECarTypes : uint8_t
|
||||
{
|
||||
Sedan, // 0
|
||||
@ -367,78 +364,78 @@ enum class ECarTypes : uint8_t
|
||||
|
||||
void WriteByteToFile(uint8_t InputValue)
|
||||
{
|
||||
// Serialize the InputValue to a file
|
||||
// Сериализуем InputValue в файл
|
||||
}
|
||||
|
||||
void WritePreferredCarTypeToFile(ECarTypes InputCarType)
|
||||
{
|
||||
// Won't compile even though ECarTypes is a uint8_t due to the enum
|
||||
// being declared as an "enum class"!
|
||||
// Хотя ECarTypes имеет тип uint8_t, код не будет скомпилирован из-за того,
|
||||
// что перечисление было объявлено как класс перечисления.
|
||||
WriteByteToFile(InputCarType);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Classes and object-oriented programming
|
||||
// Классы и объектно-ориентированное программирование
|
||||
//////////////////////////////////////////
|
||||
|
||||
// First example of classes
|
||||
// Пример классов
|
||||
#include <iostream>
|
||||
|
||||
// Declare a class.
|
||||
// Classes are usually declared in header (.h or .hpp) files.
|
||||
// Объявление класса.
|
||||
// Обычно классы объявляют в заголовочном (.h или .hpp) файле.
|
||||
class Dog {
|
||||
// Member variables and functions are private by default.
|
||||
// Переменный-члены и функции являются частными по умолчанию.
|
||||
std::string name;
|
||||
int weight;
|
||||
|
||||
// All members following this are public
|
||||
// until "private:" or "protected:" is found.
|
||||
// Все члены после этой сроки являются открытыми
|
||||
// пока "private:" или "protected:" не будет объявлено.
|
||||
public:
|
||||
|
||||
// Default constructor
|
||||
// Конструктор по умолчанию
|
||||
Dog();
|
||||
|
||||
// Member function declarations (implementations to follow)
|
||||
// Note that we use std::string here instead of placing
|
||||
// Объявление функций-членов
|
||||
// Обратите внимание, мы используем std::string здесь вместо использования
|
||||
// using namespace std;
|
||||
// above.
|
||||
// Never put a "using namespace" statement in a header.
|
||||
// выше.
|
||||
// Никогда не размещайте выражение "using namespace" в заголовке.
|
||||
void setName(const std::string& dogsName);
|
||||
|
||||
void setWeight(int dogsWeight);
|
||||
|
||||
// Functions that do not modify the state of the object
|
||||
// should be marked as const.
|
||||
// This allows you to call them if given a const reference to the object.
|
||||
// Also note the functions must be explicitly declared as _virtual_
|
||||
// in order to be overridden in derived classes.
|
||||
// Functions are not virtual by default for performance reasons.
|
||||
// Функции, которые не изменяют состояние объекта,
|
||||
// должны быть помечены как const.
|
||||
// Это позволяет вызывать их если дана const ссылка на объект.
|
||||
// Обратите внимание, функции должны быть явно объявлены как _virtual_
|
||||
// если вы хотите перегрузить их в производных классах.
|
||||
// Функции не являются виртуальными по умолчания для повышения производительности.
|
||||
virtual void print() const;
|
||||
|
||||
// Functions can also be defined inside the class body.
|
||||
// Functions defined as such are automatically inlined.
|
||||
// Такде функции могут быть определены внутри тела класса.
|
||||
// Функции, определенные следующим образом, автоматически встроены.
|
||||
void bark() const { std::cout << name << " barks!\n"; }
|
||||
|
||||
// Along with constructors, C++ provides destructors.
|
||||
// These are called when an object is deleted or falls out of scope.
|
||||
// This enables powerful paradigms such as RAII
|
||||
// (see below)
|
||||
// The destructor should be virtual if a class is to be derived from;
|
||||
// if it is not virtual, then the derived class' destructor will
|
||||
// not be called if the object is destroyed through a base-class reference
|
||||
// Наряду с конструкторами, в C++ есть деструкторы.
|
||||
// Они вызываются, когда объект удаляется или выпадает с области видимости.
|
||||
// Это активирует мощную парадигму программирования известную как RAII
|
||||
// (смотрите ниже)
|
||||
// Деструктор должен быть виртуальным, если класс будет производным.
|
||||
// Если он не виртуальный, тогда деструктор производного класса не будет вызван
|
||||
// если объект удален по ссылке или указателю базового класса.
|
||||
// or pointer.
|
||||
virtual ~Dog();
|
||||
|
||||
}; // A semicolon must follow the class definition.
|
||||
}; // Определение класса должно завершатся точкой с запятой.
|
||||
|
||||
// Class member functions are usually implemented in .cpp files.
|
||||
// Функции-члены класса, как правило, реализуются в .cpp файлах.
|
||||
Dog::Dog()
|
||||
{
|
||||
std::cout << "A dog has been constructed\n";
|
||||
}
|
||||
|
||||
// Objects (such as strings) should be passed by reference
|
||||
// if you are modifying them or const reference if you are not.
|
||||
// Объекты (такие как строки) должны передаваться по ссылке если вы будете
|
||||
// изменять их, или const-ссылке если нет.
|
||||
void Dog::setName(const std::string& dogsName)
|
||||
{
|
||||
name = dogsName;
|
||||
@ -449,7 +446,7 @@ void Dog::setWeight(int dogsWeight)
|
||||
weight = dogsWeight;
|
||||
}
|
||||
|
||||
// Notice that "virtual" is only needed in the declaration, not the definition.
|
||||
// Обратите внимание, "virtual" требуется только в объявлении, не в определении.
|
||||
void Dog::print() const
|
||||
{
|
||||
std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";
|
||||
@ -461,35 +458,34 @@ Dog::~Dog()
|
||||
}
|
||||
|
||||
int main() {
|
||||
Dog myDog; // prints "A dog has been constructed"
|
||||
Dog myDog; // Печатает "A dog has been constructed"
|
||||
myDog.setName("Barkley");
|
||||
myDog.setWeight(10);
|
||||
myDog.print(); // prints "Dog is Barkley and weighs 10 kg"
|
||||
myDog.print(); // Печатает "Dog is Barkley and weighs 10 kg"
|
||||
return 0;
|
||||
} // prints "Goodbye Barkley"
|
||||
} // Печатает "Goodbye Barkley"
|
||||
|
||||
// Inheritance:
|
||||
// Интерфейсы:
|
||||
|
||||
// This class inherits everything public and protected from the Dog class
|
||||
// as well as private but may not directly access private members/methods
|
||||
// without a public or protected method for doing so
|
||||
// Этот класс наследует все открытые и защищенные члены класса Dog
|
||||
// также как и все закрытые, но не может непосредственно получить доступ к закрытым
|
||||
// членам\методам без открытых или защищенных методов для этого.
|
||||
class OwnedDog : public Dog {
|
||||
|
||||
void setOwner(const std::string& dogsOwner);
|
||||
|
||||
// Override the behavior of the print function for all OwnedDogs. See
|
||||
// http://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping
|
||||
// for a more general introduction if you are unfamiliar with
|
||||
// subtype polymorphism.
|
||||
// The override keyword is optional but makes sure you are actually
|
||||
// overriding the method in a base class.
|
||||
// Переопределяем поведение функции печати для всех OwnedDog. Смотрите
|
||||
// https://goo.gl/3kuH2x для боле общего введения, если вы не знакомы
|
||||
// с концепцией полиморфизма подтипов (включения).
|
||||
// Ключевое слово override является необязательным, но указывает что метод
|
||||
// на самом деле перегружается в базовом классе.
|
||||
void print() const override;
|
||||
|
||||
private:
|
||||
std::string owner;
|
||||
};
|
||||
|
||||
// Meanwhile, in the corresponding .cpp file:
|
||||
// Тем временем, в соответствующем .cpp файле:
|
||||
|
||||
void OwnedDog::setOwner(const std::string& dogsOwner)
|
||||
{
|
||||
@ -498,53 +494,54 @@ void OwnedDog::setOwner(const std::string& dogsOwner)
|
||||
|
||||
void OwnedDog::print() const
|
||||
{
|
||||
Dog::print(); // Call the print function in the base Dog class
|
||||
Dog::print(); // Вызывает функцию print в базовом классе Dog
|
||||
std::cout << "Dog is owned by " << owner << "\n";
|
||||
// Prints "Dog is <name> and weights <weight>"
|
||||
// Печатает "Dog is <name> and weights <weight>"
|
||||
// "Dog is owned by <owner>"
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Initialization and Operator Overloading
|
||||
// Инициализация и перегрузка операторов.
|
||||
//////////////////////////////////////////
|
||||
|
||||
// In C++ you can overload the behavior of operators such as +, -, *, /, etc.
|
||||
// This is done by defining a function which is called
|
||||
// whenever the operator is used.
|
||||
// В C++ вы можете перегрузить поведение таких операторов: +, -, *, / и др..
|
||||
// Это делается путем определения функции, которая вызывается,
|
||||
// когда используется оператор.
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
class Point {
|
||||
public:
|
||||
// Member variables can be given default values in this manner.
|
||||
// Значения по умолчанию для переменных-членов могут быть установлены
|
||||
// следующим образом.
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
||||
// Define a default constructor which does nothing
|
||||
// but initialize the Point to the default value (0, 0)
|
||||
// Определяем новый конструктор, который инициализирует Point со значениями
|
||||
// по умолчанию (0, 0)
|
||||
Point() { };
|
||||
|
||||
// The following syntax is known as an initialization list
|
||||
// and is the proper way to initialize class member values
|
||||
// Следующий синтаксис известен как список инициализации и является верным способом
|
||||
// инициализировать значения членов класса.
|
||||
Point (double a, double b) :
|
||||
x(a),
|
||||
y(b)
|
||||
{ /* Do nothing except initialize the values */ }
|
||||
{ /* Ничего не делайте кроме инициализации значений */ }
|
||||
|
||||
// Overload the + operator.
|
||||
// Перегружаем оперот +.
|
||||
Point operator+(const Point& rhs) const;
|
||||
|
||||
// Overload the += operator
|
||||
// Перегружаем оператор +=.
|
||||
Point& operator+=(const Point& rhs);
|
||||
|
||||
// It would also make sense to add the - and -= operators,
|
||||
// but we will skip those for brevity.
|
||||
// Имеет смысл добавить перегрузку операторов - и -=,
|
||||
// но для краткости мы опустим эти детали.
|
||||
};
|
||||
|
||||
Point Point::operator+(const Point& rhs) const
|
||||
{
|
||||
// Create a new point that is the sum of this one and rhs.
|
||||
// Создает новую точку, которая является суммой этой точки и rhs.
|
||||
return Point(x + rhs.x, y + rhs.y);
|
||||
}
|
||||
|
||||
@ -558,59 +555,58 @@ Point& Point::operator+=(const Point& rhs)
|
||||
int main () {
|
||||
Point up (0,1);
|
||||
Point right (1,0);
|
||||
// This calls the Point + operator
|
||||
// Point up calls the + (function) with right as its parameter
|
||||
// Здесь происходит вызов оператора + класса Point
|
||||
// Точка "up" вызывает + (функция) с параметром "right"
|
||||
Point result = up + right;
|
||||
// Prints "Result is upright (1,1)"
|
||||
// Печатает "Result is upright (1,1)"
|
||||
cout << "Result is upright (" << result.x << ',' << result.y << ")\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Templates
|
||||
// Шаблоны
|
||||
/////////////////////
|
||||
|
||||
// Templates in C++ are mostly used for generic programming, though they are
|
||||
// much more powerful than generic constructs in other languages. They also
|
||||
// support explicit and partial specialization and functional-style type
|
||||
// classes; in fact, they are a Turing-complete functional language embedded
|
||||
// in C++!
|
||||
// Шаблоны в С++, в основном, используются для обобщенного программирования, хотя
|
||||
// они гораздо более мощны чем дженерики в других языках. Они также поддерживают
|
||||
// явные, частные и функциональные типы классов; на самом деле, они являются
|
||||
// тьюринг-полным языком встроенным в C++!
|
||||
|
||||
// We start with the kind of generic programming you might be familiar with. To
|
||||
// define a class or function that takes a type parameter:
|
||||
// Мы начнем с наиболее распространенного типа обобщенного программирования. Чтобы
|
||||
// определить класс или функцию, которая принимает параметр типа:
|
||||
template<class T>
|
||||
class Box {
|
||||
public:
|
||||
// In this class, T can be used as any other type.
|
||||
// В этом классе T может быть любого типа.
|
||||
void insert(const T&) { ... }
|
||||
};
|
||||
|
||||
// During compilation, the compiler actually generates copies of each template
|
||||
// with parameters substituted, so the full definition of the class must be
|
||||
// present at each invocation. This is why you will see template classes defined
|
||||
// entirely in header files.
|
||||
// Во время компиляции, компилятор фактически генерирует копии каждого шаблона
|
||||
// с замещенными параметрами, по-этому полное определение класса должно присутствовать
|
||||
// при каждом вызове. Именно по-этому классы шаблонов полностью определены в
|
||||
// заголовочных файлах.
|
||||
|
||||
// To instantiate a template class on the stack:
|
||||
// Чтобы создать экземпляр класса шаблона на стеке:
|
||||
Box<int> intBox;
|
||||
|
||||
// and you can use it as you would expect:
|
||||
// и вы можете использовать его, как и ожидалось:
|
||||
intBox.insert(123);
|
||||
|
||||
// You can, of course, nest templates:
|
||||
// Вы, конечно, можете использовать вложенные шаблоны:
|
||||
Box<Box<int> > boxOfBox;
|
||||
boxOfBox.insert(intBox);
|
||||
|
||||
// Until C++11, you had to place a space between the two '>'s, otherwise '>>'
|
||||
// would be parsed as the right shift operator.
|
||||
// Вплоть до С++11, вы должны были ставить пробел между двумя символами '>', иначе '>>'
|
||||
// принимался парсером, как оператор правого сдвига.
|
||||
|
||||
// You will sometimes see
|
||||
// Иногда вы можете увидеть
|
||||
// template<typename T>
|
||||
// instead. The 'class' keyword and 'typename' keywords are _mostly_
|
||||
// interchangeable in this case. For the full explanation, see
|
||||
// вместо этого. В этом случае, ключевые слова 'class' и 'typename' _в основном_
|
||||
// взаимозаменяемыми. Для более подробной информации смотрите
|
||||
// http://en.wikipedia.org/wiki/Typename
|
||||
// (yes, that keyword has its own Wikipedia page).
|
||||
// (да-да, это ключевое слово имеет собственную страничку на вики).
|
||||
|
||||
// Similarly, a template function:
|
||||
// Аналогичным образом, шаблонная функция:
|
||||
template<class T>
|
||||
void barkThreeTimes(const T& input)
|
||||
{
|
||||
@ -619,115 +615,115 @@ void barkThreeTimes(const T& input)
|
||||
input.bark();
|
||||
}
|
||||
|
||||
// Notice that nothing is specified about the type parameters here. The compiler
|
||||
// will generate and then type-check every invocation of the template, so the
|
||||
// above function works with any type 'T' that has a const 'bark' method!
|
||||
// Обратите внимание, что здесь ничего не указано о типе параметра. Компилятор
|
||||
// будет генерировать и затем проверять тип каждый вызов шаблона, по-этому
|
||||
// данная функция работает с любым типом 'T', который имеет метод 'bark'.
|
||||
|
||||
Dog fluffy;
|
||||
fluffy.setName("Fluffy")
|
||||
barkThreeTimes(fluffy); // Prints "Fluffy barks" three times.
|
||||
barkThreeTimes(fluffy); // Печатает "Fluffy barks" три раза.
|
||||
|
||||
// Template parameters don't have to be classes:
|
||||
//Параметры шаблона не должны быть классами:
|
||||
template<int Y>
|
||||
void printMessage() {
|
||||
cout << "Learn C++ in " << Y << " minutes!" << endl;
|
||||
}
|
||||
|
||||
// And you can explicitly specialize templates for more efficient code. Of
|
||||
// course, most real-world uses of specialization are not as trivial as this.
|
||||
// Note that you still need to declare the function (or class) as a template
|
||||
// even if you explicitly specified all parameters.
|
||||
// В конце концов, вы можете явно специализировать шаблоны для более эффективного
|
||||
// кода. Конечно, большинство реальных случаев использования специализации
|
||||
// не так тривиально, как это. Обратите внимание, вам все еще нужно явно объявить
|
||||
// функцию (или класс) в качестве шаблона, даже если вы явно указали все параметры.
|
||||
template<>
|
||||
void printMessage<10>() {
|
||||
cout << "Learn C++ faster in only 10 minutes!" << endl;
|
||||
}
|
||||
|
||||
printMessage<20>(); // Prints "Learn C++ in 20 minutes!"
|
||||
printMessage<10>(); // Prints "Learn C++ faster in only 10 minutes!"
|
||||
printMessage<20>(); // Печатает "Learn C++ in 20 minutes!"
|
||||
printMessage<10>(); // Печатает "Learn C++ faster in only 10 minutes!"
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Exception Handling
|
||||
// Обработка исключений
|
||||
/////////////////////
|
||||
|
||||
// The standard library provides a few exception types
|
||||
// (see http://en.cppreference.com/w/cpp/error/exception)
|
||||
// but any type can be thrown an as exception
|
||||
// Стандартная библиотека предоставляет несколько типов исключений
|
||||
// (смотрите http://en.cppreference.com/w/cpp/error/exception)
|
||||
// но, в принципе, любой тип может быть брошен в качестве исключения.
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
// All exceptions thrown inside the _try_ block can be caught by subsequent
|
||||
// _catch_ handlers.
|
||||
// Все исключения брошены внутри блока _try_ могут быть пойманы в последующем блоке
|
||||
// _catch_.
|
||||
try {
|
||||
// Do not allocate exceptions on the heap using _new_.
|
||||
// Не выделяйте память в куче для исключений с помощью ключевого слова _new_.
|
||||
throw std::runtime_error("A problem occurred");
|
||||
}
|
||||
|
||||
// Catch exceptions by const reference if they are objects
|
||||
// Поймайте исключение по константной ссылке, если они являются объектами
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
std::cout << ex.what();
|
||||
}
|
||||
|
||||
// Catches any exception not caught by previous _catch_ blocks
|
||||
// Ловит любое исключение не пойманное предыдущим блоком _catch_
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "Unknown exception caught";
|
||||
throw; // Re-throws the exception
|
||||
throw; // Повторный выброс исключения
|
||||
}
|
||||
|
||||
///////
|
||||
// RAII
|
||||
// Получение ресурса есть инициализация (RAII)
|
||||
///////
|
||||
|
||||
// RAII stands for "Resource Acquisition Is Initialization".
|
||||
// It is often considered the most powerful paradigm in C++
|
||||
// and is the simple concept that a constructor for an object
|
||||
// acquires that object's resources and the destructor releases them.
|
||||
// Программная идиома объектно-ориентированного программирования, смысл которой
|
||||
// заключается в том, что с помощью тех или иных программных механизмов получение
|
||||
// некоторого ресурса неразрывно совмещается с инициализацией, а освобождение -
|
||||
// с уничтожением объекта.
|
||||
|
||||
// To understand how this is useful,
|
||||
// consider a function that uses a C file handle:
|
||||
// Чтобы понять на сколько это полезно,
|
||||
// рассмотрим функцию, которая использует обработчик файлов в С:
|
||||
void doSomethingWithAFile(const char* filename)
|
||||
{
|
||||
// To begin with, assume nothing can fail.
|
||||
// Для начала, предположим, ничего не может потерпеть неудачу.
|
||||
|
||||
FILE* fh = fopen(filename, "r"); // Open the file in read mode.
|
||||
FILE* fh = fopen(filename, "r"); // Открываем файл в режиме чтения.
|
||||
|
||||
doSomethingWithTheFile(fh);
|
||||
doSomethingElseWithIt(fh);
|
||||
|
||||
fclose(fh); // Close the file handle.
|
||||
fclose(fh); // Закрываем обработчик файла.
|
||||
}
|
||||
|
||||
// Unfortunately, things are quickly complicated by error handling.
|
||||
// Suppose fopen can fail, and that doSomethingWithTheFile and
|
||||
// doSomethingElseWithIt return error codes if they fail.
|
||||
// (Exceptions are the preferred way of handling failure,
|
||||
// but some programmers, especially those with a C background,
|
||||
// disagree on the utility of exceptions).
|
||||
// We now have to check each call for failure and close the file handle
|
||||
// if a problem occurred.
|
||||
// К сожалению, вещи быстро осложняется обработкой ошибок.
|
||||
// Предположим fopen может потерпеть неудачу, тогда doSomethingWithTheFile и
|
||||
// doSomethingElseWithIt вернут коды ошибок если потерпят неудачу.
|
||||
// (Исключения являются предпочтительным способом обработки ошибок,
|
||||
// но некоторые программисты, особенно те, кто имеет большой опыт работы с С,
|
||||
// не согласны с аргументами о полезности исключений).
|
||||
// Теперь мы должны проверить каждый вызов на наличие ошибок и закрыть обработчик
|
||||
// файла если таковы есть.
|
||||
bool doSomethingWithAFile(const char* filename)
|
||||
{
|
||||
FILE* fh = fopen(filename, "r"); // Open the file in read mode
|
||||
if (fh == nullptr) // The returned pointer is null on failure.
|
||||
return false; // Report that failure to the caller.
|
||||
FILE* fh = fopen(filename, "r"); // Открывает файл в режиме чтения
|
||||
if (fh == nullptr) // В случае неудачи возвращаемый указатель принимает null.
|
||||
return false; // Сообщает об неудаче вызывающему.
|
||||
|
||||
// Assume each function returns false if it failed
|
||||
// Предположим, каждая функция возвращает false в случае неудачи
|
||||
if (!doSomethingWithTheFile(fh)) {
|
||||
fclose(fh); // Close the file handle so it doesn't leak.
|
||||
return false; // Propagate the error.
|
||||
fclose(fh); // Закрываем обработчик файл чтобы не было утечек
|
||||
return false; // Сообщает об ошибке.
|
||||
}
|
||||
if (!doSomethingElseWithIt(fh)) {
|
||||
fclose(fh); // Close the file handle so it doesn't leak.
|
||||
return false; // Propagate the error.
|
||||
fclose(fh); // Закрываем обработчик файл чтобы не было утечек
|
||||
return false; // Сообщает об ошибке.
|
||||
}
|
||||
|
||||
fclose(fh); // Close the file handle so it doesn't leak.
|
||||
return true; // Indicate success
|
||||
fclose(fh); // Закрываем обработчик файл чтобы не было утечек
|
||||
return true; // Указывает на успех
|
||||
}
|
||||
|
||||
// C programmers often clean this up a little bit using goto:
|
||||
// C-программисты часто упорядочивают это с помощью goto:
|
||||
bool doSomethingWithAFile(const char* filename)
|
||||
{
|
||||
FILE* fh = fopen(filename, "r");
|
||||
@ -741,18 +737,18 @@ bool doSomethingWithAFile(const char* filename)
|
||||
goto failure;
|
||||
|
||||
fclose(fh); // Close the file
|
||||
return true; // Indicate success
|
||||
return true; // Указывает на успех
|
||||
|
||||
failure:
|
||||
fclose(fh);
|
||||
return false; // Propagate the error
|
||||
return false; // Сообщает об ошибке.
|
||||
}
|
||||
|
||||
// If the functions indicate errors using exceptions,
|
||||
// things are a little cleaner, but still sub-optimal.
|
||||
// Если функции указывают на ошибки с помощью исключений, вещи становятся проще,
|
||||
// но все еще не оптимальны.
|
||||
void doSomethingWithAFile(const char* filename)
|
||||
{
|
||||
FILE* fh = fopen(filename, "r"); // Open the file in read mode
|
||||
FILE* fh = fopen(filename, "r"); // Открываем файл в режиме чтения
|
||||
if (fh == nullptr)
|
||||
throw std::runtime_error("Could not open the file.");
|
||||
|
||||
@ -761,52 +757,51 @@ void doSomethingWithAFile(const char* filename)
|
||||
doSomethingElseWithIt(fh);
|
||||
}
|
||||
catch (...) {
|
||||
fclose(fh); // Be sure to close the file if an error occurs.
|
||||
throw; // Then re-throw the exception.
|
||||
fclose(fh); // Убедитесь, что закрываете файл, если происходит ошибка.
|
||||
throw; // Затем повторно бросает исключение.
|
||||
}
|
||||
|
||||
fclose(fh); // Close the file
|
||||
// Everything succeeded
|
||||
// Успех
|
||||
}
|
||||
|
||||
// Compare this to the use of C++'s file stream class (fstream)
|
||||
// fstream uses its destructor to close the file.
|
||||
// Recall from above that destructors are automatically called
|
||||
// whenever an object falls out of scope.
|
||||
// Сравните это с использованием класса потока файла (fstream) в С++, который
|
||||
// использует свой деструктор чтобы закрыть файл. Еще раз взгляните выше,
|
||||
// деструктор вызывается автоматически, когда объект выпадает из области видимости.
|
||||
void doSomethingWithAFile(const std::string& filename)
|
||||
{
|
||||
// ifstream is short for input file stream
|
||||
std::ifstream fh(filename); // Open the file
|
||||
// ifstream определяет файловый поток
|
||||
std::ifstream fh(filename); // Открыть файл
|
||||
|
||||
// Do things with the file
|
||||
// Что-то делать с файлом
|
||||
doSomethingWithTheFile(fh);
|
||||
doSomethingElseWithIt(fh);
|
||||
|
||||
} // The file is automatically closed here by the destructor
|
||||
} // Здесь файл автоматически закрывается в деструкторе.
|
||||
|
||||
// This has _massive_ advantages:
|
||||
// 1. No matter what happens,
|
||||
// the resource (in this case the file handle) will be cleaned up.
|
||||
// Once you write the destructor correctly,
|
||||
// It is _impossible_ to forget to close the handle and leak the resource.
|
||||
// 2. Note that the code is much cleaner.
|
||||
// The destructor handles closing the file behind the scenes
|
||||
// without you having to worry about it.
|
||||
// 3. The code is exception safe.
|
||||
// An exception can be thrown anywhere in the function and cleanup
|
||||
// will still occur.
|
||||
// Это имеет _огромнейшие_ преимущества:
|
||||
// 1. Неважно, что произойдет,
|
||||
// ресурсы (в данном случае обработчик файлов) будут очищены.
|
||||
// После того, как вы правильно напишите деструктор,
|
||||
// Больше будет _не возможно_ закрыть обработчик файлов или допустить утечку.
|
||||
// 2. Обратите внимание, что код намного проще.
|
||||
// Деструктор закрывает файловый поток "за кулисами" и вам больше не нужно об
|
||||
// этом беспокоится.
|
||||
// 3. Код устойчив к исключениям.
|
||||
// Исключение может быть брошено в любом месте в функции и это никак не повлияет
|
||||
// на очистку.
|
||||
|
||||
// All idiomatic C++ code uses RAII extensively for all resources.
|
||||
// Additional examples include
|
||||
// - Memory using unique_ptr and shared_ptr
|
||||
// - Containers - the standard library linked list,
|
||||
// vector (i.e. self-resizing array), hash maps, and so on
|
||||
// all automatically destroy their contents when they fall out of scope.
|
||||
// - Mutexes using lock_guard and unique_lock
|
||||
// Весь идиоматический код на С++ широко использует RAII для всех ресурсов.
|
||||
// Дополнительные примеры включат
|
||||
// - Использование памяти unique_ptr и shared_ptr
|
||||
// - Контейнеры - стандартная библиотека связанных списков, векторы
|
||||
// (т.е. самоизменяемые массивы), хэш-карты и все остальное автоматически
|
||||
// уничтожается сразу-же, когда выходит за пределы области видимости.
|
||||
// - Ипользование мютексов lock_guard и unique_lock
|
||||
|
||||
// containers with object keys of non-primitive values (custom classes) require
|
||||
// compare function in the object itself or as a function pointer. Primitives
|
||||
// have default comparators, but you can override it.
|
||||
// Контейнеры с пользовательскими классами в качестве ключей требуют
|
||||
// функций-компаратор в самом объекте или как указатель на функцию. Примитивы
|
||||
// имеют компараторы по умолчанию, но вы можете перегрузить их.
|
||||
class Foo {
|
||||
public:
|
||||
int j;
|
||||
@ -817,81 +812,82 @@ struct compareFunction {
|
||||
return a.j < b.j;
|
||||
}
|
||||
};
|
||||
//this isn't allowed (although it can vary depending on compiler)
|
||||
// это не допускается (хотя это может варьироваться в зависимости от компилятора)
|
||||
// std::map<Foo, int> fooMap;
|
||||
std::map<Foo, int, compareFunction> fooMap;
|
||||
fooMap[Foo(1)] = 1;
|
||||
fooMap.find(Foo(1)); //true
|
||||
|
||||
/////////////////////
|
||||
// Fun stuff
|
||||
// Веселые вещи
|
||||
/////////////////////
|
||||
|
||||
// Aspects of C++ that may be surprising to newcomers (and even some veterans).
|
||||
// This section is, unfortunately, wildly incomplete; C++ is one of the easiest
|
||||
// languages with which to shoot yourself in the foot.
|
||||
// Аспекты С++, которые могут быть удивительными для новичком (и даже для некоторых
|
||||
// ветеранов). Этот раздел, к сожалению, очень неполон. С++ является одним из самых
|
||||
// простых языков, где очень легко выстрелить себе в ногу.
|
||||
|
||||
// You can override private methods!
|
||||
// Вы можете перегрузить приватные методы!
|
||||
class Foo {
|
||||
virtual void bar();
|
||||
};
|
||||
class FooSub : public Foo {
|
||||
virtual void bar(); // Overrides Foo::bar!
|
||||
virtual void bar(); // Перегружает Foo::bar!
|
||||
};
|
||||
|
||||
|
||||
// 0 == false == NULL (most of the time)!
|
||||
// 0 == false == NULL (в основном)!
|
||||
bool* pt = new bool;
|
||||
*pt = 0; // Sets the value points by 'pt' to false.
|
||||
pt = 0; // Sets 'pt' to the null pointer. Both lines compile without warnings.
|
||||
*pt = 0; // Устанавливает значение указателя 'pt' в false.
|
||||
pt = 0; // Устанавливает значение 'pt' в нулевой указатель. Обе линии проходят
|
||||
// компиляцию без ошибок.
|
||||
|
||||
// nullptr is supposed to fix some of that issue:
|
||||
// nullptr приходит на помощь:
|
||||
int* pt2 = new int;
|
||||
*pt2 = nullptr; // Doesn't compile
|
||||
pt2 = nullptr; // Sets pt2 to null.
|
||||
*pt2 = nullptr; // Не пройдет компиляцию
|
||||
pt2 = nullptr; // Устанавливает pt2 в null.
|
||||
|
||||
// There is an exception made for bools.
|
||||
// This is to allow you to test for null pointers with if(!ptr),
|
||||
// but as a consequence you can assign nullptr to a bool directly!
|
||||
*pt = nullptr; // This still compiles, even though '*pt' is a bool!
|
||||
// Существует исключение для булевых значений.
|
||||
// Это позволит вам проверить указатели с помощью if(!ptr),
|
||||
// но как следствие вы можете установить nullptr в bool напрямую!
|
||||
*pt = nullptr; // Это по прежнему проходит компиляцию, даже если '*pt' - bool!
|
||||
|
||||
|
||||
// '=' != '=' != '='!
|
||||
// Calls Foo::Foo(const Foo&) or some variant (see move semantics) copy
|
||||
// constructor.
|
||||
// Вызывает Foo::Foo(const Foo&) или некий вариант (смотрите "move semantics")
|
||||
// копирования конструктора.
|
||||
Foo f2;
|
||||
Foo f1 = f2;
|
||||
|
||||
// Calls Foo::Foo(const Foo&) or variant, but only copies the 'Foo' part of
|
||||
// 'fooSub'. Any extra members of 'fooSub' are discarded. This sometimes
|
||||
// horrifying behavior is called "object slicing."
|
||||
// Вызывает Foo::Foo(const Foo&) или вариант, но копирует только часть 'Foo' из
|
||||
// 'fooSub'. Любые другие члены 'fooSub' пропускаются. Иногда это ужасное поведение
|
||||
// называют "object slicing."
|
||||
FooSub fooSub;
|
||||
Foo f1 = fooSub;
|
||||
|
||||
// Calls Foo::operator=(Foo&) or variant.
|
||||
// Вызывает Foo::operator=(Foo&) или вариант.
|
||||
Foo f1;
|
||||
f1 = f2;
|
||||
|
||||
|
||||
// How to truly clear a container:
|
||||
// Как по-настоящему очистить контейнер:
|
||||
class Foo { ... };
|
||||
vector<Foo> v;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
v.push_back(Foo());
|
||||
|
||||
// Following line sets size of v to 0, but destructors don't get called
|
||||
// and resources aren't released!
|
||||
// В слудующей точке размер v устанавливается в 0, но деструктор не вызывается
|
||||
// и не происходит очистка ресурсов!
|
||||
v.empty();
|
||||
v.push_back(Foo()); // New value is copied into the first Foo we inserted
|
||||
v.push_back(Foo()); // Новые значения копируются в первый вставленный Foo
|
||||
|
||||
// Truly destroys all values in v. See section about temporary objects for
|
||||
// explanation of why this works.
|
||||
// Настоящие уничтожение всех значений v. Смотрите раздел о временном объекте
|
||||
// для объяснения того, как это работает.
|
||||
v.swap(vector<Foo>());
|
||||
|
||||
```
|
||||
Further Reading:
|
||||
Дальнейшее чтение:
|
||||
|
||||
An up-to-date language reference can be found at
|
||||
Наиболее полное и обновленное руководство по С++ можно найти на
|
||||
<http://cppreference.com/w/cpp>
|
||||
|
||||
Additional resources may be found at <http://cplusplus.com>
|
||||
Дополнительные ресурсы могут быть найдены на <http://cplusplus.com>
|
||||
|
Loading…
Reference in New Issue
Block a user