Конспективный материал по С++




Понятие класса, структуры, объединения и разница между ними

class struct union

класс - объединение функций (или методов) и данных в единый компонент- класс

структура - структуры тоже могу содержать функции и данные, но в структурах все данные и функции автоматически имеют доступ public а в классах в таком случае когда не указан тип доступа по умолчакнию назначается private

объединения - объединения не могут содержать функции

разница между структурами и объединениями - объединения не могут содержать функции

пример структуры которая содержит объединеие
struct s_my
{
union
{
    struct
    {
	int x,y,z;
    };
    int val[3];
};
};
в данном случае мы можем обращатся к x,y,z как к элементам массива val[0], val[1], val[2] и обратно




работа с файлами

что бы не использовать приставку std пишется

using namespace std;

следующие заголовочные файлы позволяют работать с вводом- выводом

заголовчный файл iostream.h содержит функции вывода cout, cin, endl

заголовчный файл fstream.h позволяет работать с файлами через ifstream, ofstream, fstream и функции getline

заголовчный файл stdio.h содержит функции для вывода fprintf, fscanf, printf, scanf а также работа с файлами используя FILE и функции fput, fputs, fwrite, fread




function overload prototype

перегрузка функций, прототипы функций

перегрузка функций это когда у функции одно и тоже имя, но разные типы параметров

прототипы функций, или предварительное объявление фукнций

например мы вызываем функцию в коде программы. но тело функции которую мы вызываем находится после того места в программе, где мы эту функцию вызываем, и компилятор ничего не знает об этой функции. поэтому предватилеьно объявляется имя функции с параметрами, потом мы можем вызвать функцию в любом месте программы (до вызова или после) разместить тело функции.




parameter by val & *

параметры по значению, ссылке, и указателю

в функцию параметр можно передать по значению, по ссылке и передать указатель. когда передается по значению- после выхода из функции все данные в стеке утрачиваются, стек очищается. но можно вместо значения параметра передать в стек адрес параметра в памяти, тогда при заверщении работы функции и очистке стека переменная сохраняется, так как расположена в отдельном участке памяти.




parameters by default

параметры по умолчанию, то есть когда при вызове функции не указаны параметры, используются те что указаны при объявлении, то есть параметры по умолчанию. void f_my(int val1 = 103, int val2 = 324, int val2 = 523);

причем при вызове функции можно пропустить только все последующие параметры, тогда вместо пропущенных будут использоватся параметры по умолчанию, т.е. нельзя пропустить в средине какой- то параметр.




function templates class

шаблоны функций, классов

функцию ниже можно использовать что бы ложить два числа int или float или double, то есть не надо писать три отдельных функции для складывания чисел (не надо перегружать функции).

template  T funcAdd(T val1, T val2)
{
	T val = val1 + val2;
	return val;
}




array numeric string dynamic pass to function

массивы- строковые, числовые, динамические, связанный список - передача массива в функцию

строковый массив имеет размер 2х2, и содержит строки, выделяется память для каждой строки

числовой массив любой размерности в каждой ячейке содержит числа, память может выделятся динамически при помощи оператора new

динамический массив - выделяется память при помощи оператора new для каждого элемента массива

двухсвязанный список, или односвязанный список- это обычно структура которая содержит три елемента - значение (как элемент массива), адрес на следующий элемент списка, и адрес на предыдущий элемент списка.




try catch exception handing

обработка исключений - возможная реализация

class CMyThrow
{
public:
    char szErr[256];
    CMyThrow() 
    {
	cerr << "Error!" << endl; cerr.clear(); 
	sprintf_s(szErr, 256, "Error1111!");
    }
};

int i = 300;
try
{
    if(i < 255) cout << "Value Ok!" << endl;
    else
    throw CMyThrow();
}
catch (CMyThrow &er)
{
    cout << er.szErr << endl;
}





while do while for switch case if else

циклы, условия, применяются для управления ходом программы




const_cast static_cast dynamic_cast reinterpret_cast

const_cast убирает спецификатор const и volatile

const char *str = "hello";
char *str1 = const_cast(str);

static_cast небезопасное приведение вниз по иерархии наследования; используется для приведения одного типа к другому. Если это встроенные типы- используются правила приведения встроенные в С++, если это пользовательский тип- используются правила приведения определенные программистом. для указателей это корректное приведение, когда один из них void, например malloc() возвращает void: преобразует выражения одного статического типа в объекты и значения другого статического типа, используется для приведения от базового к наследуемому классу, если приведение не удалось вниз по иерархии результат будет undefined.

int *p = static_cast(malloc(100));

dynamic_cast безопасное приведение вниз по иерархии наследования в том числе и для виртуального наследования. Используется RTTI (Runtime Type Information) что бы привести один указатель на обеъкт класса к другому указателю на объект класса. Обязательное условие - классы должны быть полиморфными, то есть должна быть хоть одна виртуальная функция. Иначе ошибка компиляции, оператор dynamic_cast является частью механизма динамической идентификации типа данных, который позволяет выполнять приведение типа данных.

reinterpret_cast самое простое приведение в том смысле что не делается никаких проверок, результат может быть не корректным. не может быть приведено одно значение к другому значению. Обычно используется что бы привести указатель к указателю, указатель к целому, целое к указателю

разница между dinamic_cast и static_cast

в dinamic_cast используется информация от типе времени выполнения RTTI и осуществляется проверка на возможность приведения типов(это безопасное приведение с проверкой), в static_cast информация RTTI не используется (небезопасное приведение типов). главное условие для dinamic_cast хотя бы один класс в иерархии наследования дожен быть полиморфным, то есть содержать хотя бы одну виртуальную функцию.




shift operators << >> logical multiplication &

Функция подсчета единиц в байте (нужен сдвиг и лог. умножение)


void main (void)
{
	unsigned char cVal = 0xAD;
	string vResStr;

	for (int i = 0; i < 8; i++)
	{
		
		unsigned char cResVal = cVal & 0x1;

		if(cResVal == 0)
			vResStr+= '0';
		if(cResVal == 1)
			vResStr+= '1';

		cVal = cVal >> 1;
	}

	printf("%s", vResStr.c_str());

	while(!_kbhit()) {};
}





Оператор логического умножения & используеться для выделения или маскирования информации. Предположим нам надо выделить последний значимый шестнадцатиричный разряд из четырехзначного числа:


0x1234    0001  0010  0011  0100
&         &
0x000f    0000  0000  0000  1111
=         =
0x0004    0000  0000  0000  0100

if (( Val & 1011) == 0010)
{
  ....................
}






recursion


int Recursion(int val)
{
	if(val>1)
	return val*Recursion(val-1);
	return 1;
}






local global static variable scope of variables

Локальные переменные при выходе из функции теряют свое значение, глобальные сохраняют и дальше.

Пока переменная не проинициализирована, то есть ей не присвоено значение, то ее значение не предсказуемо для локальных переменных.




bubble sort sorting algiritms

пузырьковая сортировка


for(int i = 0; i < NUM_ELEMENTS-1; i++)
{
	for(j = i+1; j < NUM_ELEMENTS; j++)
	{
		if(arr[j] < arr[i])
		{
		temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
		}
	}
}






public protected private

модификаторы доступа

модификаторы доступа используются что бы определить какие из вне класса другие классы, функции из других классов могут иметь доступ к этому классу.

В любом классе:

public - члены класса доступны за пределами класса любым функциям программы (открытый режим доступа);

protected - члены класса доступны производным и дружественным классам (защищенный режим доступа);

private - члены класса доступны только для методов этого класса и классов друзей (закрытый режим доступа);

Если после открывающей фигурной скобки после имени класса не стоит никакой спецификатор (public, protected, private) - то члены класса по умолчанию становяться private (закрытыми).

Конструктор по умолчанию это конструктор не требующий параметров.




encapsulation inheritance polymorphism

инкапсуляция - сокрытие данных и методов от внешнего доступа, объединение данных и функций в единый компонент- класс, используется с private

наследование - на базе одного класса позволяет создавать наследуемые классы с расширенными возможностями, для наследования используется слово public

Наследование:

class Student {...};
calss GraduateStudent : public Student {...};

полиморфизм - использование виртуальных функций, одно имя функции разные классы

Множественное наследование
Полиморфный класс
Абстрактный класс
Виртуальная функция
Чисто виртуальная функция
Виртуальный деструктор.
Виртуальное наследование.




explicit - для конструкторов с параметрами, для явного приведения параметра; в основном применяеться для конструкторов с одним параметром

class SomeClass {
public:
  SomeClass( int );
  //explicit SomeClass(int);
};
 
int main() {
  SomeClass a( 5 ); //явный вызов
  SomeClass b = 5; //неявный вызов; если конструктор помечен explicit, то тут будет ошибка
 
  return 0;
}







константная функция int my_func() const; константная функция не может менять данные класса;

mutable - используеться для класов в которых есть функции const; mutable означает, что спецификатор const, примененный к классу, следует игнорировать. По стандарту только данные класса могут быть mutable.




volatile - для обновления переменной например в цикле; когда выполняеться цикл, переменная для производительности кешируеться, то есть не обновляеться; тут например другой поток меняет эту переменную- но она уже кеширована, то есть используеться старый образец переменной, кешированный; что бы обновить кешированную переменную- то есть она не кешируеться вобще, а при каждом цикле читаеться ее значение- используеться volatile

Компилятор скорее всего оптимизирует код вроде такого, если переменная cancel не менялась в теле цикла.


bool cancel = false;
while( !cancel ) {
    ;
}



Если cancel не меняется, то ее и проверять каждый раз незачем, компилятор и не будет ее проверять.

Зато если вы укажете перед переменной volatile, то оптимизиации не будет. Предполагается, что переменная cancel могла измениться каким-то волшебным образом (например из другого потока).


volatile bool cancel = false;
while( !cancel ) {
  ;
} 






инициализация переменных в конструкторе


CA::CA() : m_cRef(1), m_pUnknownInner(NULL)
{
.....................
}



Student noName;
Student freshMan("Smell E. Fish");

Последний объект Student был обявлен со скобками, а noName нет потому что если написать Studen noName(); то будет объявлена функция возвращающая по значению объект класса Student ( а не конструктор по умолчанию).

Если в классе есть конструктор то применяються скобки так:

Checking c1 = new Checkin(124); Checking c2 = new Checking();

Если в классе нету конструктора, то он создаеться С++ автоматически.

Деструктор класса объявляеться со знаком тильды "~" так как это знак отрицания то есть обратно конструктору. Деструкторы вызываються в порядке, обратному порядку вызова конструкторов. Вызываеться деструктор автоматически при разрушении объекта. Если объект создавался динамически через указатель или при помощи оператора new, то для уничтожения такого объекта следует использовать оператор delete для указателя. Деструктор всегда не имеет ни типа ни параметров. Конструктор и деструктор вызываються в программе автоматически при объявлении объектов класса. Деструктор базового класса может быть объявлен виртуальным, что бы при разрушении объектов производных классов вызывались их деструкторы:


class COrganization
{
public:
COrganization();
virtual ~COrganization();
};



Конструктор, который используеться С++ для создания копии объекта называеться копирующим конструктором. Копии создаються тогда когда объекты передаються в функцию по значению или при возврате из функции объекта по значению.

Конструктор по умолчанию
Конструктор с одним параметром
Конструктор с параметрами
Копирующий конструктор

Явный вызов констурктора
Неявный вызов конструктора

Виртуальная функция - это функция член, объявленная в базовом классе и переопределенная в производном. В производном классе эта функция тоже будет виртуальной. Класс, содержащий хотя бы одну виртуальную функцию называеться полиморфным. Чистая виртуальная функция - это виртуальная функция, которая не имеет реализации в базовом классе. Класс, в котором объявлена хотя бы одна чистая виртуальная функция называеться абстрактрым. Для таких классов невозможно создать объекты. Абстрактные классы могут использоваться только в качестве базовых классов для других классов. Если в производном классе, который наследует от базового абстрактного класса, отсутствует реализация чистой виртуальной функции, то этот производный класс тоже будет являться абстрактным. Объекты конструируються только для классов, в которых чистая виртуальная функция имеет реализацию.




виртуальное наследование

class COrganization {...};
class CHighSchool : virtual public COrganization {...};
class CEnterprise : virtual public COrganization {...};
class CUnion : public CHightSchool, public CEnterprise {...};

Производные классы CHighSchool и CEnterprise виртуально наследуют от базового класса COrganiztion, что бы в производном классе CUnion не было копий подобъектов базового класса COrganization. Сначала вызывается конструктор класса CEnterprise затем конструктор класса CHightSchool затем CUnion. Деструкторы вызываются в обратном порядке начиная с класса CUnion.




Если в операторе new при создании объекта класса ничего не указано, или указаны пустые круглые скобки- вызываеться конструктор по умолчанию. Если в скобках указаны параметры- вызываеться конструктор с параметрами. Если в скобках имя объекта- вызываеться конструктор копирования.




Класс содержащий хотя бы одну виртуальную функцию называеться полиморфным. Полиморфизм - использование виртуальных функций. Виртуальная функция- функция определнная в базовом классе и переопределенная в производном. Чистая виртуальная фунция не имеет реализации в базовом классе, она реализуеться в производном классе

Абстрактный базовый класс - класс содержащий хотя бы одну чисто виртуальную функцию.

Виртуальные функции называються виртуальными так как они могут существовать только в пространстве классов, то есть в виртуальном пространстве.




Используя ключевое слово friend, класс может сообщить C++, кто является его другом, т. е. другими словами, что другие классы могут обращаться напрямую к его частным элементам (private element).


class abbott 
{ 
public: 
friend costello; 
// Общие элементы 
private: 
// Частные элементы 
};







Ограничение доступа к private функциям:


class book 

{ 
public: 
   book(char *, char *, char *); 
   void show_book(void); 
   friend char *librarian::get_catalog(book); 
   friend void librarian::change_catalog( book *, char *); 
private: 
   char title[64]; 
   char author[ 64 ]; 
   char catalog[64]; 
}; 

Как видите, операторы friend содержат полные прототипы всех дружественных функций, которые могут напрямую обращаться к частным элементам- к другим, не описанным не могут.




В отличии от динамического связывания есть статическое или ранее связывание. Есть функции которые обладают ранним связыванием. Т.е. на этапе компиляции для этой функции выделяется участок памяти, а первый адрес этого участка становится адресом функции. Адрес функции жёстко привязан к имени функции - их нельзя отделить.

Есть функции которые обладают динамическим (dynamic) или поздним связыванием (late binding). На какую функцию указывает указатель, становится известно только во время выполнения программы. При этом functionPointer может указывать на любую функцию, т.е. значение указателя functionPointer может меняться во время выполнения программы. Это и есть позднее связывание.

Для виртуальных методов память выделяется точно так же, как и для обычных: на этапе компиляции под эти методы выделяются участки памяти, первые адреса которых являются адресами методов.

Когда в базовом классе объявляется хотя бы одна виртуальная функция, то для всех полиморфных классов создаётся таблица виртуальных функций (virtual function table).

Таблица виртуальных функций - это одномерный массив указателей на функции. Количество элементов в массиве равно количеству виртуальных функций в классе.

Для каждого полиморфного класса (базового и всех производных) создаётся своя таблица виртуальных методов. Количество элементов во всех этих таблицах одинаковое.

Именно в таблице виртуальных функций записываются настоящие адреса методов, т.е. элемент таблицы является указателем на функцию. Для всех полиморфных классов таблицы виртуальных функций будут содержать разные значения. Для каждого класса здесь будут записаны адреса методов данного класса.




перегрузка операторов

struct My3DVector3
{
	float x;
	float y;
	float z;

	My3DVector3() {};
	My3DVector3(float inx, float iny, float inz)
	{
		x = inx;
		y = iny;
		z = inz;
	};
	My3DVector3 operator + (My3DVector3 v)
	{
		My3DVector3 vr;
		vr.x = x + v.x;
		vr.y = y + v.y;
		vr.z = z + v.z;
		return vr;
	};
	My3DVector3 operator - (My3DVector3 v)
	{
		My3DVector3 vr;
		vr.x = x - v.x;
		vr.y = y - v.y;
		vr.z = z - v.z;
		return vr;
	};


};






работа с указателем на строку


int main(int argc, char* argv[])
{
	char *charArray = "Hello";
	char *ptr = &charArray[0];

	while (*ptr)
	{
		ptr++;
	}

	cin.get();

	return 0;
}






функцмя выводит на экран строку символов


void print_my_string(char  arr[])
{
	cout << arr << endl;
	cout << arr[1] << endl;
}

void main (void)
{
	char *szBuff1 = {"Hello C++ #1"};

	char szBuff2[256] = {"Hello C++ #2"};

	print_my_string(szBuff1);
	print_my_string(szBuff2);

	while (!_kbhit()){};
}







работа с указателями, выделение пямяти new и удаление указателей delete

void main(void)
{
//создание переменной указателя на двухмерный массив
int **nval;

//выделение памяти
nval = new int*[10];

//выделение памяти
for (int i=0; i<10; i++)
{
nval[i] = new int[10];
}

//заполнение значениями от 0 до 100
int index = 0;

for (int i=0; i<10; i++)
{
	for (int j=0; j<10; j++)
	{
		nval[i][j]= index;
		index++;
	}
}

//вывод на экран
for (int i=0; i<10; i++)
{
	for (int j=0; j<10; j++)
	{
		cout << nval[i][j] << endl;
		
	}
}

//удаление указателей
for (int i=0; i<10; i++)
delete [] nval[i];

//удаление указателей
delete [] nval;

while(!_kbhit()){};
}







создание массива символьных строк


void main(void)
{
//создание переменной указателя на двухмерный массив
char **nval;

//выделение памяти
nval = new char*[10];

//выделение памяти
for (int i=0; i<10; i++)
{
nval[i] = new char[256];
}

//заполнение значениями 
for (int i=0; i<10; i++)
{
	char szBuff[256], szVal[256];
	strcpy(szBuff, "Hello #");
	itoa(i, szVal, 10);
	strcat(szBuff, szVal);
	strcpy(nval[i],szBuff);
}

//вывод на экран
for (int i=0; i<10; i++)
{
		cout << nval[i] << endl;
}

while(!_kbhit()){};

//удаление указателей
for (int i=0; i<10; i++)
delete [] nval[i];

//удаление указателей
delete [] nval;

while(!_kbhit()){};
}






массив указателей, выделение памяти, уничтожение указателей

void main(void)
{
int *p1;
int *p2[4];
char szBuff[256];
//-----------------------
p1 = new int[4];

p1[0] = 1;
p1[1] = 2;
p1[2] = 3;
p1[3] = 4;

sprintf_s(szBuff, 256, "%d-%d-%d-%d", p1[0], p1[1], p1[2], p1[3]);

cout << szBuff << endl;

delete [] p1;
//-----------------------------
p2[0] = new int(1);
p2[1] = new int(2);
p2[2] = new int(3);
p2[3] = new int(4);

sprintf_s(szBuff, 256, "%d-%d-%d-%d", *p2[0], *p2[1], *p2[2], *p2[3]);

cout << szBuff << endl;

delete p2[0];
delete p2[1];
delete p2[2];
delete p2[3];

while (!_kbhit()){};

}







Сравнение двух чисел float (чисел с плавающей запятой)


const float Epsilon = 0.001f;
bool Equals(float lhs, loat rhs)
{
return fabs (lhs – rhs) < Epsilon ? true : false;
}






cout << 10 / 3 << endl; //будет 3
cout << 4.0/3.0 << endl: //будет 1.333333
cout << 4/3.0 << endl: //будет 1.333333

Если складывать int и double в С++ то результат будет более мощным то есть double:

double fVal3 = 1.0;
int nVal2 = 10;
double fVal1 = nVal2 + fVal3;

Результат будет 11.0 то есть с плавающей точкой. Но можно использовать явное приведение:

int nVal4 = nVal2 + (int)fVal3;

Тогда результат будет целым числом 11;

Если тип с плавающией точкой присвоить значению int то результат будет округлен до целого:

float fVal1 = 10.1;
int nVal2 = (int)fVal1;

Результат будет число 10;




В С++ принято давать имена переменным согласно их содержанию и начинаються они со строчной буквы (венгерская нотация):
pPointer  указатель;
nVal  целое число;
lVal  long значение;
fVal  float значение (действительные числа);
dVal  double значение;
cVal  character (одна буква, символ);
szVal  строка символов





Оператор break; можна использовать для выхода из циклов; когда используеться оператор continue; то программа немедленно переходит к началу цикла;

while (true) {...}; как и for(;;) {...}; это бесконечные циклы;




Перегрузка фукнций это когда несколько функций с одним и тем же именем, но с разными параметрами:

void someFunc(void) {};
void someFunc(int n) {};
void someFunc(float f) {};


В зависимости от параметра вызываеться нужная функция. В консольной программе нужно обяъвлять прототипы функций перед функцией main() что бы при вызове перегруженой функции было известно какую из них именно вызывать.




Что бы получить размер памяти, занимаемой например int в нашем компиляторе следует:

int nSizeInt = sizeof(int);




найти конец строки
while (*ptr)  //заметте в этой строке используеться *ptr
{
	ptr++;  //а в этой строке просто указатель ptr
}





Повторные объявления можна избежать:

#ifndef MyModule_h
#define MyModule_h
//Сюда можна вставлять все что нужно разместить во включаемом файле
//Закрытие #ifndef в конце файла
#endif





кроме того есть директива
#pragma once
class CBook
{
....
};





обявление массива и инициализация указателя

nt *pArray = new int[20] - объявление массива из 20 чисел

int *pMyInt = new int(20) - инициализация указателя числом 20

DWORD *pMyArray = new DWORD[100];
delete []pMyArray;




Когда программы передают параметр в функцию, то по умолчанию С++ делает копию значения параметра и помещает эту копию во временный участок памяти, называемый стеком. Затем функция использует копию значения для выполнения своих операций. Когда функция завершается, C++ сбрасывает содержимое стека и все изменения, сделанные функцией в копиях эначений параметра. Как вы знаете, переменная представляет собой имя, присваиваемое вашей программой ячейке памяти, которая хранит значение определенного типа. Если вы передаете параметры по адресу, C++ помещает адрес каждой переменной в стек.Для изменения значения параметра в функции, функция должна знать адрес параметра в памяти. Следовательно, ваша программа должна передать адрес параметра с помощью оператора адреса C++; Внутри функции вы должны сообщить C++ , что функция будет работать с адресом памяти (указателем). Для этого при объявлении вы предваряете имя параметра звездочкой;




Инициализация массива при объявлении:

int values[5] = { 100, 200, 300, 400, 500 };




Символьные строки:
char title[64] = "Учимся программировать на языке C++";
Если количество символов, присваиваемое строке, меньше размера массива, большинство компиляторов C++ будут присваивать символы NULL остающимся элементам строкового массива.
char title[] = "Учимся программировать на языке C++";

передача строк в функцию
#include  

void show_string(char string[]) 

{ 
   cout << string << endl; 
} 

void main(void) 

{ 
   show_string("Привет, C++!"); 
   show_string("Учусь программировать на C++"); 
}