init commit

This commit is contained in:
2025-04-13 21:48:15 +03:00
commit 23255e9121
192 changed files with 12200 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
## Мини-отступление
```cpp
#include <utility>
C f() {
C local var;
// return local_var - криво
return std::move(local_var)
}
```
Благодаря std::move можно не создавать вторую переменную для возвращаемого значения. Входит в стандарт с c++17 и оптимизируется автоматически в c++14.
---
# Перегрузка операций
## Дружеские функции
```cpp
class C {
int a;
public:
...
friend void f(C &x);
};
void f(C &x) {cout << x.a}
```
функция также может относиться к другому классу
## Перегргузка операций
Нельзя перегружать:
- \#
- ::
- ?:
- .
- .\*
- sizeof
- typeid
### Бинарные операции
```cpp
class C{
double re;
double im;
public:
C (double x = 0.0, double y = 0.0) : re(x), im(y) {}
bool isEqual(C &obj) {
return re == obj.re && im == obj.im; // неявная подстановка this->
}
bool operator ==(C &obj) {
return re == obj.re && im == obj.im;
}
// нужен const, чтобы можно было делать o3 = o1 + 10.0
C operator+(const C& obj) {
return C(re + obj.re, im + obj.im);
} // o3 = o1 + o2
};
void main() {
C o1, o2, o3;
o1.isEqual(o2) == (o1 == o2); // the same
o3 = o1 + o2;
o3 = o1 + 10.0
}
```
либо для дальнейшей работы и `o1 = 10.0 + o2` изменим перегрузку операнда:
```cpp
friend C operator+(const C& obj1, const C& obj2) {
return C(obj2.re + obj1.re, obj2.im + obj1.im);
}
```
### Унарные операции
```cpp
C operator-() {
return C(-re, -im);
} // o3 = -o1
// !! не перегрузит никак бинарную
```
Посткремент и инкремент:
```cpp
C& operator++() {
re++;
im++;
return *this;
}
C operator++(int NU) { // not used var
С tmp(re, im);
re++;
im++;
return tmp;
}
void main() {
o3 = ++o1;
o3 = o1++;
}
```
`o3 = (o1++)++;` не проработает!! (изменит временную переменную)
### Перегрузка =, [], ()
> не перегружаются как дружественные!
```cpp
C &operator=(const C &obj) {
re = obj.re;
im = obj.im;
return *this;
} // по умолчанию так перегружается
```
## Итоговый пример
```cpp
int
main() {
string s1("abcd"), s2("qw"), s3;
s2[1] = 'A';
s2.print();
s3 = s1 + s2;
s3.print();
}
```
Итоговый код, чтобы условие выше выполнялось:
```cpp
#include <string.h>
#include <cstring>
#include <utility>
#include <iostream>
class string
{
int len; // количество памяти
char* str;
public:
string(const char* s = "") {
len = strlen(s) + 1;
str = new char[len];
strcpy(str, s);
}
char&
operator[](int n) {
return str[n];
}
string
operator+(string s) {
string tmp;
tmp.len = s.len + len - 1;
tmp.str = new char[tmp.len];
strcpy(tmp.str, str);
strcpy(tmp.str + len - 1, s.str);
return tmp;
}
string
operator=(string s) {
if (s.str == str) {
return s;
}
delete[] str;
len = s.len;
str = new char[len];
strcpy(str, s.str);
return *this;
}
// ~string() { delete[] str; } почему-то с ним не работает (?)
void
print() {
std::cout << str << " " << len << std::endl;
}
};
```
версия присваивания получше:
```cpp
void string::swap(string& a) {
std::swap(str, a.str);
std::swap(len, a.len);
}
string& string::operator=(string a) {
swap(a);
return *this;
}
```
+204
View File
@@ -0,0 +1,204 @@
#
## Про оптимизации
```cpp
A obj = A(10); // A(const A&)
obj1 = obj; // A(A&)
```
при отключении оптимизации будет:
- преобразования
- копирования
- деструктор
На экзе во всех задачах _вероятно_ отключена оптимизация
```cpp
A f(...) {
A local;
return local;
}
```
будет создаваться 2 переменные.
## Перегрузка (продолжение)
### Приведение типов
```cpp
operator double() {
return re;
}
double x = 3.1, y;
y = C1 + x; // !error - не понятно, к какому типу
y = double(C1) + x == C1 + C(x);
```
### Ввод/вывод
```cpp
friend ostream & operator<<(ostream &os, C&a) {
os << "re=" << a.re << ...;
return os;
}
int main() {
C obj(1,2);
cout << obj; // ostream and class
return 0;
}
```
### Перегрузка вызова функции
```cpp
struct Pred {
bool operator()(int v) {
return v < 10;
}
};
void f(int *mas, int n, Pred(c)) {
for (int i = 0; i < n; ++i) {
if (c(mas[i])) {
mas[i] *= 2;
}
}
}
```
---
## Перегрузка функций
Алгоритм для поиска наиболее подходящей функции для вызова
1. по количеству параметров
2. отбросить недопустимые преобразования
3. точные совпадения (на typedef):
1. T <-> T&
2. T[] <-> \*T
3. T -> const T
4. можно расширить тип (например к инт):
1. unsigned char
2. unsigned short
3. short and etc
4. float -> double (нет лонг дабл)
5. стандартные преобразования
- Любой числовой приводится к любому числовому типу
- Любой указатель приводится к указателю на void
- 0 к любому указателю или любому числовому
6. пользовательсткие преобразования
---
```cpp
const int ci = 1;
void f(int y) {...}
f(ci); // всё ок
```
---
алгоритм (фактически):
- отбрасываем неподходящие
- по каждому параметру берём множество подходящих функций
- смотрим персечение
```cpp
struct A {
operator int() { return 1; }
void f(double d, char c); // 1
void f(double d, int j); // 2
void f(A a, const char *p); // 3
void f(int i, const char *p); // 4
};
int main() {
A a;
f(a, 0); // 3 & 2 => 0
f(a, 'a'); // 12 & 1 => 1
f('a', 0); // 4 & 2 => 0
}
```
## Статические члены класса
```cpp
class A {
int a;
static int b;
public:
A(int x) : a(x) { }
static void f() {
cout << b;
}
};
int main() {
int A::b = 77;
A::f();
return 0;
}
```
требования:
- работа только с статик полями
- вызывать только статик функции
- нельзя как const или virtual объявить (void f const { } // может менять статик и mutable поля)
```cpp
class A {
static int x;
int y;
public:
int f(int x) const {};
};
int main() {
const A a;
a.x = 3;
cout << a.y << a.x << a.f(1);
}
```
---
```cpp
struct A {
int n;
int k;
A (A &a) { n = a.n; }
int f() { return k; }
A (int a) { n = a; }
};
int main() {
A a = A(1), b;
cout << A::f() << " " << a.k << a.n << endl; // 5 53
}
```
```cpp
struct A {
int n;
static int k;
A (const A &a) { n = a.n; }
static int f() { return k; }
A (int a = 3) { n = a; }
};
int A::k = 5;
int main() {
A a = A(1), b;
cout << A::f() << " " << a.k << b.n << endl; // 5 53
}
```
+259
View File
@@ -0,0 +1,259 @@
# Третий семинар
## Шаблоны
```cpp
// template <typename>
template <class T>
void swap(T& a, T& b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
```
Можно применять и от нескольких неизвестных параметров
```cpp
template <typename T, typename K>
void transfer(T fromAccount, T toAccount, K code, int sum)
{
std::cout << "From: " << fromAccount << "\nTo: " << toAccount
<< "\nSum: " << sum << "\nCode: " << code << std::endl;
}
```
Пример кода для абсолютного значения:
```cpp
template <class T> T abs(const T& x) {
// template <class T> T abs(T x) {
return x >= 0 ? x : -x;
}
```
Суммирование элементов массива
```cpp
template <class T> T sum(T* mas, int N) {
T res = 0;
for (int i = 0; i < N; ++i) {
res += mas[i];
}
return res;
}
int main() {
double mas[5] = {...};
std::cout << sum(mas, 5);
return 0;
}
```
```cpp
template <class T> T sum(T* mas, int N) {
T res = 0;
for (int i = 0; i < N; ++i) {
res += mas[i];
}
return res;
}
int main() {
double mas[5] = {...};
std::cout << sum<double, 5>(mas);
return 0;
}
```
## Что-то про стэк
Вторая задача:
Суть была реализовывать динамически расширяемый массив
```cpp
template<class T, int SIZE>
class st {
T st[SIZE];
int top = 0;
public:
void push(T a) {
if (top == SIZE) {
// error
} else {
st[top++] = a;
}
}
T pop();
}
template<class T, int SIZE>
T st<T, SIZE>::pop() {
return st[--top];
}
```
Если указать стандартное значение для каких-то параметров, вызов обычный
## Библиотеки
### Контейнеры
#### Последовательности
- list - двунаправленный список
- vector
- deque - очередь
На их основе создаются:
- stack
- queue
#### Ассоциативные массивы
- map - отображения
- multimap - ключ может несколько раз встречаться
- set - уникальные упорядоченные ключи
- multiset - совпадающие ключи
### Итераторы
`#include <algorithm>` - подключение библиотеки
- value_type - тип значений в контейнере
- size_type - аналог size_t
- reference/ const reference - ссылки на элемент
- pointer/const pointer - указатели
Исполняют роль указателей
Для каждого контейнера определён:
- iterator - идёт от начала
- reverse_iterator - идёт с конца
- const_iterator - не меняет значений
- const_reverse_iterator - не меняет значений, ревёрс
```cpp
begin(), end();
rbegin(), rend();
cbegin(), cend(); // для константных
```
Виды итераторов:
- input
- output
- forward
- bidirectional - list, map, set
- random_access - для vector deque, можно индексировать, += n
```cpp
#include <vector>
int main() {
vector<int> v1; // длина 0
vector<int> v2(5); // длина 5, занулены
vector<int> v3(10, '*');
int m[] = {1, 2, 3, 4};
vector<int> v4(m, m + 4);
// vector<int> v4(m.begin(), m.end());
}
```
```cpp
for (int i = 0; i < v.size(); ++i)
cout << v[i] << " "; // только для random_access контейнера
v[i] == v.at(i) // выдаёт out_of_range, если нет элемента
```
Печать элементов в прямом и обратном при помощи итераторов
```cpp
#include <vector>
#include <iostream>
// прямой с помощью прямого
void f1(std::vector<int> mas) {
std::vector<int>::std::iterator iter = mas.begin();
while (iter != mas.end()) {
std::cout << *iter << " ";
iter++;
}
}
// обратный с помощью реверсивного
void f2(std::vector<int> mas) {
std::vector<int>::std::reverse_iterator iter = mas.rbegin();
while (iter != mas.rend()) {
std::cout << *iter << " ";
iter++;
}
}
// обратный с помощью прямого
void f3(std::vector<int> mas) {
std::vector<int>::std::iterator iter = mas.end();
while (iter != mas.begin()) {
--iter;
std::cout << *iter << " ";
}
}
```
Или же код нормального человека:
```cpp
for (auto x : v) cout << x << " ";
```
### Алгоритмы
Некоторые методы контейнеров:
- `size_type size()` - количество элементов в контейнере
- `bool empty()` - пустой ли контейнер
- `void clear()` - очищает контейнер
- `void push_back()` - добавляет в конец value_type<>
- `void pop_back()` - удаляет последний элемент
- `reference back()` - возвращает ссылку на последний элемент
- `reference front()` - возвращает ссылку на первый элемент
- `iterator insert(iterator p, value_type<>)` - вставка перед позицией p, возвращает на новый элемент
- `iterator erase(iterator p)` - удаляет, куда указывает, возвращает итератор на некст элемент
- `iterator erase(iterator p1, iterator p2)` - c первого до последнего (не включительно)
для list, deque
- `void push_front(value_type<>)`
- `void pop_front(value_type<>)`
Для bidirectional итераторов можно использовать
- `advance(p1, 3)` для плюса
- `n = distance(p1, p2)` для вычитания
```cpp
void f(vactor<char>& mas) {
auto iter = mas.begin();
while (iter < mas.end()) {
++iter;
if (iter != mas.end())
iter = mas.erase(iter);
}
}
```
+180
View File
@@ -0,0 +1,180 @@
# Шаблонные функции с контейнером на вход
-> написать шаблонную функцию, которая ищет макс значение в константном или неконстантном контейнере
```cpp
#include <iostream>
#include <list>
#include <vector>
using namespace std;
template <typename T>
typename T::value_type Max(const T& c) {
// используем константные итераторы cbegin
typename T::value_type max = *c.cbegin();
for (const auto& it : c) {
if (*it > max) {
max = *it;
}
max = *it > max ? *it : max
}
return max;
}
int main() {
list<int> l = { 1, 2, 3, 4, 5 };
cout << Max<list<int>>(l) << endl;
vector<long> v = { 1, 2, 3, 4, 5 };
cout << Max<vector<long>>(v) << endl;
return 0;
}
```
Шаблонная функция, которая удваивает все элементы в контейнере
Из [1, 2, 3] делает [1, 1, 2, 2, 3, 3]
```cpp
// работает, но херня, много памяти жрёт
template <typename T>
void dbl(T& c) {
T res;
auto it = c.begin();
while (it != c.end()) {
res.push_back(*it);
res.push_back(*it);
++it;
}
c = res;
return;
}
// работает, всё правильно
template <typename T>
void dbl(T& c) {
auto it = c.begin();
// typename T::iterator it = c.begin(); - порой могут вот так просить на коллоквиуме
while (it != c.end()) {
it = c.insert(++it, *it);
++it;
}
return;
}
```
вход функции: 2 итератора, реверсирующая данную коллекцию
Было [1, 2, 3] Стало [3, 2, 1]
```cpp
template <typename T>
void rever(T b, T e) {
if (b == e) return; // для проверки на пустой контейнер
--e;
while (b != e) { // просто сравнивать нельзя у листов
swap(*b++, *e); // нативный свап
if (b == e) break;
--e;
}
return;
}
```
функции подаётся 2 итератора, предикат и стандартное значение
Предикат - объект с перегруженными круглыми скобками
```cpp
template<typename T, typename P> // T - указатель P - предикат
void f(T b, T, e, P pred, typename T::value_type val) {
}
// пример предиката
struct Pred {
bool operation()(int v) {
return v % 3;
}
}
int main () {
vector<int> v = {...};
f(v.begin(), v.end(), Pred(), 9);
return 0;
}
```
## Лямба-функции
Неименованные функции, которые можно просто вставить в выражение
```cpp
int x = 5;
auto ef = [x]() { cout << x; } // тут не обойтись без auto
ef();
auto ef = [](int a) { cout << a; }
ef(x);
```
В квадратных скобках - привязка к внешним элементам
- [] привязки нет
- [=] привязываются все внешние
- [&] по ссылке привязываются все внешние
В круглых - к тем, что передаются
## Алгоритм remove_if
```cpp
vector <int> v = {2, 4, 5, 7, 9, 10};
auto end = remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; })
```
remove_if перемещает в хвост все значения, для которых предикат (лямбда-функция) верна. Возвращает указатель на начало хвоста
Вывод: [5, 7, 9, 2, 4, 10]. end указывать будет на '2'
## 2 задание 3го контеста
> есть уже complex, complex stack. Добавление в стек через операцию <<
посчитать польско-инвер запись для комплексных чисел. Если попадается z - значение по умолчанию
> map - словарик. Содержит элементы 'ключ' - 'значение'
```cpp
complex eval(const vector<string>& args, const complex& z) {
complex stack st;
map<string, function<void()>> mp; {
{"z", [&st, &z]() { st = st << z; }}, // если попалось z, добавили в стек
{";", [&st]() { st = ~st; }},
... // все прочие +/-/... операции
}
for (const auto& iter : args) {
if (s[0] != '(') { // если строка не (re,im)
mp[s](); // просто вызвали нужное
} else {
st = st << complex(s);
}
}
// ответ на верхушке стека
return +st;
}
```
BIN
View File
Binary file not shown.
+61
View File
@@ -0,0 +1,61 @@
# Наследования
```cpp
class A {
int a;
public:
A(int x = 0) : a(x) {}
void f() { cout << a; }
};
class B : public/private/protected A/*, C*/ { // private by default
int b;
public:
B(int x = 0, int y = 0) : A(x), b(y) {}
void f() {
cout << a << b; // error. A is private
}
};
```
```cpp
int main() {
A obj_a, *pa;
B obj_b, *pb;
pa = &obj_a;
pb = &obj_b;
// при открытом наследовании
pa = pb;
pb = (B*)pa;
// при закрытом наследовании
pa = (A*)pb;
// pb = (B*)pa; - невозможно
*pa = &obj_a;
pa->f(); // вызов из a
*pa = &obj_b;
pa->f(); // вызов тоже из a, т.к. не виртуальная функция
}
```
в ситуации любого наложения функций, происходит перекрытие имён
```cpp
class A {
void f();
}
class B : A {
void f(int) { f(); } // error
}
```
конструкторы не наследуются
## консруктор копирования
```cpp
B (const B &a) : A(a) {}
```
+187
View File
@@ -0,0 +1,187 @@
# Семинар 6
## RTTI - Run Time Type Identification
- typeid
- dynamic_cast
### typeid
> `typeid` возвращает тип объекта/указателя. Если указатель на класс с виртуальными функциями, вернёт именно тип объекта. Иначе - указателя.
Для `type_info` переопределённые операции и методы:
- ==
- !-
- .name
```cpp
class A {
public:
virtual void f() {}
};
class B : public A {};
int main () {
A *pa = new B;
B *pb;
std::cout << typeid(*pa).name; // 1B (1 символ в имени)
if (typeid(*pa) == typeid(B)) {
pb = (B*)pa; // небезопасное приведение
}
}
```
## Dynamic cast
```cpp
class A {
public:
virtual void f() {}
};
class B : public A {};
int main () {
A *pa, a;
B *pb, b;
pa = &b;
// безопасно приводит, вернёт не NULL в случае успеха
pb = dynamic_cast<B*>(pa);
}
```
```cpp
class A {
public:
virtual void f() {}
};
class B : public A {};
int main () {
A *pa, a;
B *pb, b;
A &pr = a;
// B &rb = dynamic_cast<B&>(pa); // выкинет bad_cast
}
```
## Исключения
`try - throw - catch`
`terminate()` - в случае, если ничего не обработалось
приводится в `catch` указатели к константным могут
```cpp
int f() {
try {
...
throw 10;
...
throw "abc";
...
throw A();
...
} catch (int a) {
...
throw ...; // тогда обработается функцией выше
...
} catch (const char* s) {
...
} catch (A &x) {
...
} catch (...) { // все остальные исключения
}
}
```
В случае возвращение класса, который наследуется от базового, "ловить" надо сначала производный, после - базовый
Деструктор для создаваемого класса вызывается только после фулл обработки исключения (если catch сам вызвал throw)
```cpp
int f() throw(int, char) {
try {
...
throw 1.2;
...
} catch (double) {
...
}
}
```
`unexpected()` - вызывается, если мы на внешнюю функцию передаём исключение типа, который не указан
```cpp
// никакие не передавать вовне
int f() throw();
after c++17 only:
int f() noexcept;
```
### Алгоритм обработки исключений
- создание временного объекта типа возвращаемого исключения
- уничтожение объектов в `try` блоке
- выход из `try`
- подбор нужного обработчика
- если больше исключений нет - деструктор временного объекта
- если не обработано - выход к внешнему обработчику
```cpp
5.14. Что напечатает следующая программа?
struct S {
S ( int a) {
try {
if (a > 0) throw *this ;
else if (a < 0) throw 0;
}
catch ( S & ) {
cout << SCatch_S& << endl;
} catch (int ) { throw ; }
cout << SConstr << endl;
}
S (const S & a) { cout << Copy << endl; }
~S ( ) { cout << Destr << endl; }
};
int main ( ) {
try {
S s1( 0 ), s2 ( 5 );
cout << Main << endl;
}
catch (S &) { cout << MainCatch_S& << endl; }
catch ( ... ) { cout << MainCatch_... << endl; }
return 0;
}
```
Результат отработки программы:
```text
Sconstr
Copy
Scatch_s&
Destr
Sconstr
Main
Destr
Destr
```
1
14
15
CatchB
bad_cast
63
63
3
...
End
+66
View File
@@ -0,0 +1,66 @@
#include <string.h>
#include <cstring>
#include <utility>
#include <iostream>
class string
{
int len; // количество памяти
char* str;
public:
string(const char* s = "") {
len = strlen(s) + 1;
str = new char[len];
strcpy(str, s);
}
char&
operator[](int n) {
return str[n];
}
string
operator+(string s) {
string tmp;
tmp.len = s.len + len - 1;
tmp.str = new char[tmp.len];
strcpy(tmp.str, str);
strcpy(tmp.str + len - 1, s.str);
return tmp;
}
// string
// operator=(string s) {
// if (s.str == str) {
// return s;
// }
// delete[] str;
// len = s.len;
// str = new char[len];
// strcpy(str, s.str);
// return *this;
// }
// ~string() { delete[] str; } почему-то с ним не работает (?)
void
print() {
std::cout << str << " " << len << std::endl;
}
};
int
main() {
string s1("abcd"), s2("qw"), s3;
s2[1] = 'A';
s2.print();
s3 = s1 + s2;
s3.print();
}
+96
View File
@@ -0,0 +1,96 @@
// -> написать шаблонную функцию, которая ищет макс значение в константном или неконстантном контейнере
#include <iostream>
#include <list>
#include <vector>
using namespace std;
template <class Container>
typename Container::value_type Max(const Container& c) {
typename Container::value_type max = *c.cbegin();
for (auto it = c.cbegin(); it != c.cend(); ++it) {
if (*it > max) {
max = *it;
}
}
return max;
}
template <typename T>
void dbl(T& c) {
auto it = c.begin();
while (it != c.end()) {
it = c.insert(++it, *it);
++it;
}
return;
}
template <typename T>
void rever(T b, T e) {
if (b == e) return;
--e;
while (b != e) {
swap(*b++, *e);
if (b == e) break;
--e;
}
return;
}
struct Pred {
bool operator()(int x) {
return x % 3;
}
};
template<typename T, typename P> // T - указатель P - предикат
void f(T b, T e, P pred, typename T::value_type val = {}) {
while (b != e) {
if (pred(*b)) {
*b = val;
}
++b;
}
return;
}
int main() {
list<int> l = { 1, 2, 3, 4, 5 };
cout << Max<list<int>>(l) << endl;
f(l.begin(), l.end(), Pred());
for (const auto& elem : l) {
cout << elem << " ";
}
cout << endl;
// dbl(l);
// rever(l.begin(), l.end());
// for (const auto& elem : l) {
// cout << elem << " ";
// }
// cout << endl;
vector<long> v = { 1, 3, 3, 4, 5 };
cout << Max<vector<long>>(v) << endl;
// dbl(v);
rever(v.begin(), v.end());
for (const auto& elem : v) {
cout << elem << " ";
}
cout << endl;
return 0;
}
+119
View File
@@ -0,0 +1,119 @@
# Markdown Cheat Sheet
Thanks for visiting [The Markdown Guide](https://www.markdownguide.org)!
This Markdown cheat sheet provides a quick overview of all the Markdown syntax elements. It cant cover every edge case, so if you need more information about any of these elements, refer to the reference guides for [basic syntax](https://www.markdownguide.org/basic-syntax/) and [extended syntax](https://www.markdownguide.org/extended-syntax/).
## Basic Syntax
These are the elements outlined in John Grubers original design document. All Markdown applications support these elements.
### Heading
# H1
## H2
### H3
### Bold
**bold text**
### Italic
*italicized text*
### Blockquote
> blockquote
### Ordered List
1. First item
2. Second item
3. Third item
### Unordered List
- First item
- Second item
- Third item
### Code
`code`
### Horizontal Rule
---
### Link
[Markdown Guide](https://www.markdownguide.org)
### Image
![alt text](https://www.markdownguide.org/assets/images/tux.png)
## Extended Syntax
These elements extend the basic syntax by adding additional features. Not all Markdown applications support these elements.
### Table
| Syntax | Description |
| ----------- | ----------- |
| Header | Title |
| Paragraph | Text |
### Fenced Code Block
```
{
"firstName": "John",
"lastName": "Smith",
"age": 25
}
```
### Footnote
Here's a sentence with a footnote. [^1]
[^1]: This is the footnote.
### Heading ID
### My Great Heading {#custom-id}
### Definition List
term
: definition
### Strikethrough
~~The world is flat.~~
### Task List
- [x] Write the press release
- [ ] Update the website
- [ ] Contact the media
### Emoji
That is so funny! :joy:
(See also [Copying and Pasting Emoji](https://www.markdownguide.org/extended-syntax/#copying-and-pasting-emoji))
### Highlight
I need to highlight these ==very important words==.
### Subscript
H~2~O
### Superscript
X^2^