TGViewer
Channel Public Channel
C/C++ | Вопросы собесов

C/C++ | Вопросы собесов

@easy_c_plus

Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @sendme_ads
Subscribers
4.2K
Photos
36
Videos
1
Links
1.6K
Recent Posts 20 shown
Post #2558 158
🤔 Строгая гарантия безопасности

Гарантии безопасности исключений в C++ делятся на три уровня:
Базовая гарантия (Basic Guarantee) – программа не падает, но объект может остаться в некорректном состоянии.
Строгая гарантия (Strong Guarantee) – либо операция завершается успешно, либо объект остаётся в том же состоянии, что и до вызова.
Гарантия отсутствия исключений (No-Throw Guarantee) – операция никогда не бросает исключений.

🚩Что такое строгая гарантия безопасности исключений?

Строгая гарантия (Strong Guarantee) означает, что если во время выполнения операции выбрасывается исключение, объект остаётся неизменным. Это похоже на транзакции в базе данных – "всё или ничего".
#include <iostream>
#include <vector>

class BadContainer {
std::vector<int> data;
public:
void add(int value) {
data.push_back(value); // push_back() может выбросить исключение
}

void print() {
for (int x : data) std::cout << x << " ";
std::cout << std::endl;
}
};

int main() {
BadContainer c;
c.add(1);
c.add(2);
c.add(3);
c.print(); // Вывод: 1 2 3

try {
c.add(42); // Может выбросить исключение (например, нехватка памяти)
} catch (...) {
std::cout << "Ошибка!\n";
}

c.print(); // ❌ Возможно, состояние испорчено!
}


🚩Как реализовать строгую гарантию?

Чтобы добиться строгой гарантии, используем Copy & Swap:
1. Создаём временный объект.
2. Выполняем изменения на временном объекте.
3. Если всё прошло успешно – меняем указатель (swap).
#include <iostream>
#include <vector>

class SafeContainer {
std::vector<int> data;
public:
void add(int value) {
std::vector<int> temp = data; // Создаём копию текущего состояния
temp.push_back(value); // Добавляем элемент в копию
std::swap(data, temp); // Если исключения не было, меняем данные
}

void print() {
for (int x : data) std::cout << x << " ";
std::cout << std::endl;
}
};

int main() {
SafeContainer c;
c.add(1);
c.add(2);
c.add(3);
c.print(); // Вывод: 1 2 3

try {
c.add(42); // Если тут исключение, объект не изменится
} catch (...) {
std::cout << "Ошибка!\n";
}

c.print(); // ✅ Вывод: 1 2 3 (не испорчен!)
}


🚩Где используется строгая гарантия?

Операции присваивания (operator=) с Copy & Swap
Методы стандартных контейнеров (std::vector::resize, std::map::insert)
Функции стандартной библиотеки (std::sort)
Изменения объектов с ресурсами (файлы, сокеты, память)

Ставь 👍 и забирай 📚 Базу знаний
Post #2557 273
🤔 Выбрасывание исключения из конструктора — это нормально?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2556 269
🤔 Какое преимущество у list перед vector?

List обеспечивает быстрые вставки и удаления за \(O(1)\) в произвольной позиции, благодаря двусвязной структуре. В отличие от vector, list не требует перераспределения памяти при добавлении элементов. Однако доступ к элементам в list медленнее, так как отсутствует прямой индексированный доступ.

🚩Основные преимущества `std::list`

🟠Эффективные вставка и удаление элементов в середине контейнера
std::list реализован как двусвязный список, что позволяет эффективно вставлять и удалять элементы в любом месте контейнера за постоянное время O(1), если у вас уже есть итератор на нужное место.
#include <list>
#include <iostream>

int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
auto it = myList.begin();
std::advance(it, 2); // Переместить итератор на третий элемент

myList.insert(it, 10); // Вставка за O(1)
myList.erase(it); // Удаление за O(1)

for (int n : myList) {
std::cout << n << " ";
}

return 0;
}


🟠Нет необходимости в перемещении элементов при вставке или удалении
В std::vector при вставке или удалении элементов в середине контейнера происходит сдвиг элементов, что может быть дорогостоящей операцией (сложность O(n)). В std::list этого не происходит, так как элементы не хранятся в смежных блоках памяти.

🟠Постоянное время для операций вставки и удаления в произвольной позиции
Операции вставки и удаления в произвольной позиции в std::list всегда выполняются за O(1) (при условии, что у вас есть итератор на эту позицию), в то время как в std::vector они могут занимать до O(n) из-за необходимости сдвига элементов.

🟠Итераторы не инвалидируются при вставке или удалении элементов
В std::vector итераторы могут быть инвалидированы (становятся недействительными) при вставке или удалении элементов. В std::list итераторы остаются валидными, что делает работу с итераторами более безопасной и предсказуемой.

🚩Примеры, когда `std::list` предпочтительнее

🟠Частые вставки и удаления в середине контейнера
Если ваша задача предполагает частые операции вставки и удаления в середине контейнера, std::list будет предпочтительнее из-за своей эффективности в этих операциях.

🟠Работа с большим количеством итераторов
Если вам нужно работать с множеством итераторов, которые должны оставаться валидными даже после вставки или удаления элементов, std::list будет лучше подходить для таких задач.

🚩Ограничения `std::list`

🟠Доступ по индексу
В отличие от std::vector, в std::list нет возможности быстрого доступа к элементам по индексу. Для доступа к элементу необходимо пройти весь список от начала до нужного элемента (сложность O(n)).

🟠Высокие накладные расходы на память
std::list требует больше памяти на хранение элементов, так как для каждого элемента хранится два дополнительных указателя (на следующий и предыдущий элементы).

Ставь 👍 и забирай 📚 Базу знаний
Post #2554 293
🤔 Как работает priority_queue?

priority_queue управляет элементами на основе их приоритетов, используя бинарную кучу: на вершине всегда элемент с наивысшим приоритетом, а операции добавления и удаления имеют сложность O(log n).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2553 284
🤔 Что такое placement new?

placement new – это специальная форма оператора new, которая размещает объект в уже выделенной памяти вместо того, чтобы выделять её заново.
placement new
НЕ выделяет память (мы сами передаём адрес).
Просто вызывает конструктор в указанном месте.
void* ptr = malloc(sizeof(int));   // Выделяем память вручную
int* num = new(ptr) int(42); // Размещаем объект в этой памяти


Синтаксис placement new
new (адрес) Type(аргументы конструктора);


адрес – указатель на уже выделенную память.
Type(аргументы конструктора) – объект создаётся в этом месте.
#include <iostream>

int main() {
char buffer[sizeof(int)]; // Буфер памяти (размер как у int)

int* p = new (buffer) int(123); // Размещаем int в этом буфере

std::cout << "Значение: " << *p << std::endl; // 123
}


🚩Когда используется `placement new`?

🟠Оптимизация выделения памяти
Например, если у нас часто создаются и удаляются объекты, но память уже есть. Не нужно каждый раз вызывать new и delete.

🟠Размещение объектов в особых областях памяти
Например, на стеке (char buffer[100]), в shared memory, или в выделенной заранее области (malloc).

🟠Создание объекта в заранее выделенном массиве
Стандартный std::vector использует placement new внутри себя!

🟠Кастомные аллокаторы (`std::allocator`)
Например, в играх и в embedded-разработке, где управление памятью критично.

🚩`placement new` и классы

Если объект – это класс, placement new вызывает его конструктор в выделенной памяти.
#include <iostream>
#include <cstdlib> // Для malloc/free

class MyClass {
public:
int x;
MyClass(int val) : x(val) { std::cout << "Конструктор!\n"; }
~MyClass() { std::cout << "Деструктор!\n"; }
};

int main() {
void* buffer = malloc(sizeof(MyClass)); // Выделяем сырую память
MyClass* obj = new (buffer) MyClass(42); // Размещаем объект

std::cout << "Значение: " << obj->x << std::endl;

obj->~MyClass(); // Ручной вызов деструктора!
free(buffer); // Освобождаем память
}


Вывод
Конструктор!
Значение: 42
Деструктор!


🚩Ошибки при `placement new`

Забыл вызвать деструктор
MyClass* obj = new (buffer) MyClass(10);
// delete obj; // НЕЛЬЗЯ! Обычный `delete` не работает
free(buffer); // Память утекла! Деструктор не вызвался


Нужно вызвать вручную
obj->~MyClass();
free(buffer);


placement new в одном буфере несколько раз (без очистки)*
void* buffer = malloc(sizeof(MyClass));
MyClass* obj1 = new (buffer) MyClass(1);
MyClass* obj2 = new (buffer) MyClass(2); // Перезапишет obj1!


Нужно сначала вызвать деструктор
obj1->~MyClass();
MyClass* obj2 = new (buffer) MyClass(2);


Освободил память перед вызовом деструктора
free(buffer);  // Память освободили
obj->~MyClass(); // ОШИБКА! Память уже не существует


Правильный порядок
obj->~MyClass();
free(buffer);


Ставь 👍 и забирай 📚 Базу знаний
Post #2551 359
🤔 Какое присваивание разрешает unique_ptr?

unique_ptr поддерживает только перемещение (move assignment), чтобы гарантировать уникальное владение объектом, но не копирование.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2550 372
🤔 Какую операцию над вектором можно сделать, которая вызовет много копирования?

В C++ есть несколько операций над std::vector, которые могут привести к множественному копированию элементов. Рассмотрим основные из них.

🟠Изменение размера (`resize`)
Метод resize() может вызвать множественное копирование, если новый размер вектора превышает его текущую вместимость (capacity).
#include <iostream>
#include <vector>

struct Data {
int value;
Data(int v) : value(v) {}
Data(const Data& other) { // Конструктор копирования
value = other.value;
std::cout << "Copying Data: " << value << std::endl;
}
};

int main() {
std::vector<Data> vec(3, Data(10)); // Заполняем 3 элементами
std::cout << "Resizing..." << std::endl;
vec.resize(10, Data(20)); // Вектор расширяется, возможны копирования
}


🟠Вставка элемента (`insert` и `emplace`)
Если std::vector не имеет достаточного запаса (capacity), вставка нового элемента может привести к реаллокации и копированию всех элементов.
std::vector<int> vec = {1, 2, 3};
vec.insert(vec.begin(), 0); // Вставка в начало → все элементы сдвигаются


🟠Присваивание (`operator=`)
Если один std::vector присваивается другому, все элементы копируются.
std::vector<int> vec1 = {1, 2, 3, 4, 5};
std::vector<int> vec2;
vec2 = vec1; // Копирование всех элементов


🟠Возвращение `std::vector` из функции
Если возвращаем std::vector по значению, может произойти копирование (если не работает оптимизация RVO/NRVO или перемещение).
std::vector<int> createVector() {
std::vector<int> vec(100, 42);
return vec; // Может скопироваться, если нет RVO
}


Ставь 👍 и забирай 📚 Базу знаний
  • 👍 2
Post #2548 348
🤔 Знаешь правило 5ти и правило 0?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2547 339
🤔 Что такое наследование?

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

🚩Основные аспекты

🟠Повторное использование кода
Позволяет производным классам использовать код базового класса. Это способствует снижению дублирования кода и увеличивает его повторное использование.

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

🟠Иерархия
Вводит естественную организацию, создавая иерархическую структуру классов. Это упрощает понимание отношений между различными классами и их взаимодействия.

🟠Полиморфизм
Является ключом к полиморфизму, где методы производного класса могут переопределять методы базового класса. Это позволяет использовать производные объекты вместо базовых объектов, что делает программу более модульной.

🚩Типы

🟠Одиночное наследование
Класс наследуется от одного базового класса.

🟠Множественное наследование
Класс может наследоваться от более чем одного класса. Не все языки поддерживают множественное наследование из-за сложности разрешения различных аспектов, таких как ромбовидное наследование (diamond problem).

🟠Многоуровневое наследование
Формируется когда класс наследуется от другого производного класса, формируя многоуровневую структуру.

#include <iostream>
using namespace std;

// Базовый класс
class Animal {
public:
void eat() {
cout << "I can eat!" << endl;
}
};

// Производный класс
class Dog : public Animal {
public:
void bark() {
cout << "I can bark! Woof woof!" << endl;
}
};

int main() {
Dog myDog;
myDog.eat(); // Вызов метода базового класса
myDog.bark(); // Вызов метода производного класса
return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2541 376
🤔 Что такое noexcept?

`noexcept` — это спецификатор в C++, который указывает, что функция не будет выбрасывать исключений. Это помогает компилятору оптимизировать код и предотвратить ненужные проверки на выброс исключений. Функции, помеченные как `noexcept`, могут быть безопасно вызваны в контексте, где выбрасывание исключений может привести к сбою программы. Использование `noexcept` также улучшает производительность в ситуациях, связанных с исключительным управлением.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 🔥 1
Post #2540 400
🤔 Расскажи про константные методы?

Это методы класса, которые гарантируют, что они не будут изменять состояние объекта, для которого вызываются. Эти методы можно вызывать на константных объектах и через константные ссылки или указатели. Ключевое слово const после объявления метода указывает, что метод является константным.

🚩Объявление и определение константного метода

Чтобы объявить метод как константный, нужно добавить ключевое слово const после списка параметров метода:
class MyClass {
public:
int getValue() const; // Константный метод
void setValue(int newValue); // Неконстантный метод

private:
int value;
};

// Определение константного метода
int MyClass::getValue() const {
return value; // Допустимо: метод не изменяет состояние объекта
}

// Определение неконстантного метода
void MyClass::setValue(int newValue) {
value = newValue; // Допустимо: метод изменяет состояние объекта
}


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

#include <iostream>

class MyClass {
public:
MyClass(int v) : value(v) {}

int getValue() const { // Константный метод
return value;
}

void setValue(int newValue) { // Неконстантный метод
value = newValue;
}

private:
int value;
};

int main() {
MyClass obj(10);

std::cout << "Value: " << obj.getValue() << std::endl; // Вывод: 10

obj.setValue(20);
std::cout << "New value: " << obj.getValue() << std::endl; // Вывод: 20

// Константный объект
const MyClass constObj(30);
std::cout << "Const object value: " << constObj.getValue() << std::endl; // Вывод: 30

// constObj.setValue(40); // Ошибка: setValue не может быть вызван для константного объекта

return 0;
}


🚩Почему и когда использовать константные методы

🟠Гарантия неизменности
Константные методы гарантируют, что состояние объекта не будет изменено. Это полезно для методов, которые предназначены только для чтения данных объекта, например, методы доступа (геттеры).
🟠Безопасность и семантика
Константные методы помогают лучше понимать код и повышают его безопасность, так как ясно видно, какие методы могут изменять состояние объекта, а какие нет.
🟠Работа с константными объектами
Константные методы можно вызывать на константных объектах и через константные ссылки или указатели. Это позволяет использовать объекты в контексте, где важно гарантировать их неизменность.

🚩Вызов константных методов на константных объектах

const MyClass constObj(30);
constObj.getValue(); // Допустимо: getValue — константный метод
// constObj.setValue(40); // Ошибка: setValue — неконстантный метод


Ставь 👍 и забирай 📚 Базу знаний
Post #2539 407
🤔 Принцип Open/Closed (открытости/закрытости)?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2538 418
🤔 Как работает range based?

Range-based for loop – это упрощённый цикл for, который позволяет перебирать элементы контейнера (std::vector, std::array, std::map, std::set и т. д.) без индексов и итераторов.
for (auto element : container) {
// Действие с element
}


🚩Как это работает внутри?

Простой пример с std::vector
#include <iostream>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};

for (int x : v) { // Перебираем все элементы вектора
std::cout << x << " ";
}
}


Вывод
1 2 3 4 5


Как работает этот цикл?
Компилятор превращает его в обычный for с итератором:
for (auto it = v.begin(); it != v.end(); ++it) {
int x = *it; // Копируем элемент
std::cout << x << " ";
}


🚩Передача по ссылке (`&`) и по значению (`=`)

Передача по значению (=) – создаёт копию элемента
for (int x : v) { // x - копия элемента
x = 100; // НЕ изменит вектор!
}


Передача по ссылке (&) – изменяет оригинал
for (int& x : v) { // x - ссылка на элемент
x *= 2; // Изменит оригинальный вектор!
}


🚩Работает со всеми контейнерами STL

С std::map
std::map<int, std::string> m = {{1, "one"}, {2, "two"}};
for (const auto& [key, value] : m) { // structured binding (C++17)
std::cout << key << " -> " << value << "\n";
}


С std::set
std::set<int> s = {1, 2, 3, 4};
for (int x : s) { std::cout << x << " "; }


🚩Работает с `std::initializer_list`

for (int x : {10, 20, 30}) {
std::cout << x << " ";
}


Вывод
10 20 30


🚩Как работает range-based for с обычными массивами?

int arr[] = {1, 2, 3};
for (int x : arr) { std::cout << x << " "; }

Работает так же, как и с `std::vector`!

Работает с пользовательскими классами (если есть begin() и end())
class MyContainer {
int data[3] = {10, 20, 30};
public:
int* begin() { return data; }
int* end() { return data + 3; }
};

int main() {
MyContainer c;
for (int x : c) { std::cout << x << " "; }
}


Ставь 👍 и забирай 📚 Базу знаний
  • 🤔 1
Post #2537 443
🤔 Как удалить элемент из начала vector, если порядок не важен?

Вместо сдвига всех элементов (O(n)), можно:
1. Переместить последний элемент на место первого (O(1)).
2. Удалить последний элемент (pop_back()).
Это нарушит порядок, но значительно ускорит удаление.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 1
Post #2536 466
🤔 Как называется одинаковый результат после применения хэш функции?

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

🚩Что такое коллизия?

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

🚩Почему коллизии возникают?

🟠Ограниченный размер хэш-значений
Хэш-функция генерирует значения фиксированной длины (например, 32 или 64 бита), поэтому существует конечное количество возможных хэшей. Однако количество входных данных, которые можно подать на вход, теоретически бесконечно.
🟠Особенности алгоритма хэширования
Некоторые хэш-функции менее устойчивы к коллизиям из-за их структуры.

🚩Как справляться с коллизиями?

Есть несколько способов обработки коллизий в хэш-таблицах:

🟠Метод цепочек (chaining)
Каждое значение хэша хранит список элементов, которые получили одинаковый хэш. При добавлении нового ключа он просто добавляется в связанный список.
#include <iostream>
#include <list>
#include <vector>
using namespace std;

class HashTable {
int size;
vector<list<int>> table;

public:
HashTable(int s) : size(s), table(s) {}

int hashFunction(int key) {
return key % size;
}

void insert(int key) {
int index = hashFunction(key);
table[index].push_back(key);
}

void display() {
for (int i = 0; i < size; i++) {
cout << i << ": ";
for (int key : table[i]) {
cout << key << " -> ";
}
cout << "NULL" << endl;
}
}
};

int main() {
HashTable ht(5);
ht.insert(10);
ht.insert(15);
ht.insert(20);
ht.insert(7);
ht.insert(2);
ht.display();
return 0;
}


🟠Открытая адресация (open addressing)
При коллизии новое значение записывается в следующую свободную ячейку в таблице. Используются разные стратегии, такие как линейное пробирование, квадратичное пробирование или двойное хэширование.

🟠Использование криптографических хэш-функций
Устойчивые хэш-функции (например, SHA-256) уменьшают вероятность коллизий, но они медленнее и чаще используются в безопасности.

Ставь 👍 и забирай 📚 Базу знаний
Post #2535 462
🤔 В чем разница set и unordered_set?

Set — это отсортированное множество, которое хранит элементы в порядке возрастания и использует бинарное дерево для внутренней реализации. Unordered_set хранит элементы в произвольном порядке и использует хеш-таблицу для доступа к элементам. В set операции поиска, вставки и удаления имеют сложность O(log n), а в unordered_set — O(1) в среднем случае, но O(n) в худшем случае. Set предпочтителен, когда требуется поддерживать порядок элементов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 1
Post #2534 453
🤔 Когда возникает самый худший случай поиска в unordered_set?

В unordered_set, который основан на хеш-таблице, самый худший случай поиска возникает, когда структура данных деградирует до такой степени, что время поиска становится линейным, то есть \(O(n)\), где \(n\) — количество элементов в контейнере. Это происходит по нескольким причинам:

🟠Плохая хеш-функция
Играет ключевую роль в распределении элементов по "ведрам" (buckets) в unordered_set. Если хеш-функция плохо разрабатывается и генерирует одинаковые или близкие хеш-коды для большого количества ключей, элементы начинают накапливаться в очень небольшом числе ведер. Это приводит к тому, что большинство операций поиска, вставки и удаления начинают зависеть от числа элементов в самом длинном связном списке, а не от общего количества ведер, что сильно ухудшает производительность.

🟠Высокая степень загрузки
Загрузка хеш-таблицы (load factor) — это отношение количества элементов в хеш-таблице к количеству ведер. Когда load factor становится слишком высоким, вероятность коллизий увеличивается, что также приводит к увеличению длины цепочек в каждом ведре. Хотя unordered_set автоматически увеличивает количество ведер при увеличении количества элементов, в случаях с экстремально высоким load factor поиск может деградировать до линейного.

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

Ставь 👍 и забирай 📚 Базу знаний
Post #2532 421
🤔 Что будет, если вызвать оператор delete на nullptr?

Если вызвать оператор delete на nullptr, ничего не произойдет. Это безопасная операция, так как стандарт гарантирует, что освобождение nullptr не вызовет ошибок или исключений.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 3
Older posts →

About this channel

How can I read @easy_c_plus without a Telegram account?
TGViewer shows the public web preview Telegram publishes for C/C++ | Вопросы собесов: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does C/C++ | Вопросы собесов have?
C/C++ | Вопросы собесов (@easy_c_plus) has 4.2K subscribers on Telegram, refreshed roughly every 30 minutes.
Does C/C++ | Вопросы собесов know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →