Основы языка C++ (2)

12. В чем разница между диким указателем и оборванным указателем? Как этого избежать?

Дикий указатель (дикий указатель): это указатель, который не был инициализирован. Скомпилируйте с помощью gcc -Wall, появится предупреждение useduninitialized.

Висячий указатель : это указатель, на который была освобождена память, на которую первоначально указывал указатель.
Является ли это диким указателем или висячим указателем, это указатель на недопустимую область памяти (недопустимый здесь означает «небезопасный и неуправляемый»). Доступ к «небезопасным и контролируемым» (недопустимым) областям памяти приведет к «неопределенному поведению».
Как избежать диких указателей? В обычном кодировании выработайте привычку завершать инициализацию после определения указателя и перед его использованием или используйте интеллектуальные указатели.

13. Поговорите о том, как отличить константные указатели

Ниже приведены все юридические заявления, но они имеют очень разные значения:

const int * p1; //指向整形常量的指针,它指向的值不能修改
int * const p2; //指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。
const int *const p3; //指向整形常量 的 常量指针 。它既不能再指向别的常量,指向的值也不能修改。

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

14. Кратко об указателях на функции

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

char * fun(char * p) {
    
    ...} // 函数fun
char**pf)(char * p); //函数指针pf
pf = fun;                     // 函数指针pf指向函数fun
pf(p);                       //通过函数指针pf调用函数fun

15. Разница между кучей и стеком

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

В большинстве компиляторов параметры помещаются в стек справа налево (причина такого порядка заключается в том, чтобы программистам было удобнее использовать функцию «переменной длины параметров функции» C/C++. Если в стек помещаются слева направо первый параметр (то есть параметр, описывающий тип переменной каждой таблицы параметров переменных) будет помещен в нижнюю часть стека, поскольку на первом шаге функции параметров переменных необходимо разрешить переменную.

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

Стек расширяется от старших адресов к младшим, и в стеке есть младшие и старшие адреса, а пространство мало.

куча

Управляемый программистом, он должен быть выделен и переработан вручную с помощью new malloc delete free.Если он не переработан, это вызовет утечку памяти.

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

Куча расширяется от младшего адреса к старшему, с большим пространством и большей гибкостью.

16. Несколько способов передачи параметров функции

Передача по значению : формальный параметр является копией фактического параметра, и операция над формальным параметром внутри функции не повлияет на внешний фактический параметр.
Передача по указателю: это также способ передачи по значению.Формальный параметр — это указатель на адрес фактического параметра.При работе с указателем формального параметра это эквивалентно работе с самим фактическим параметром.
Передача по ссылке : фактически адрес ссылочного объекта помещается в открытое пространство стека, и любая операция над формальными параметрами внутри функции может быть напрямую сопоставлена ​​с внешними фактическими параметрами.

17 разница между new/delete/, malloc/free

Оба могут использоваться для выделения и освобождения места в куче. new /delete — операторы, malloc и free — библиотечные функции.

Выполнение new фактически выполняет два процесса : 1. Выделение неинициализированного пространства памяти (malloc) 2. Использование конструктора объекта для инициализации пространства, возвращение начального адреса пространства. Если возникнет проблема с выделением места на первом шаге, будет выброшено исключение std::bad_alloc, либо оно будет перехвачено установленной функцией обработки исключений; если исключение возникнет на втором шаге при построении объекта, delete будет автоматически освобождать память.
Выполнение удаления на самом деле состоит из двух процессов : 1. Использование деструктора для уничтожения объекта 2. Освобождение места в памяти (свободного).

В приведенном выше примере также видна разница между new и malloc: new получает инициализированное пространство , а malloc получает неинициализированное пространство . Таким образом, new — это тип new, а malloc — это пространство с длиной malloc. delete и free одинаковы, delete не только освобождает пространство, но также уничтожает объекты, удаляет тип и освобождает пространство размером в байт.

Зачем нам нужно новое/удаление с помощью malloc/free? Потому что для невнутренних типов данных один только malloc/free не может удовлетворить требования динамических объектов. Объект должен автоматически выполнять конструктор при его создании, а объект должен автоматически выполнять деструктор до того, как он умрет . Поскольку mallo/free является библиотечной функцией, а не оператором, она не находится в сфере контроля компилятора, и задача выполнения конструктора и деструктора не может быть возложена на malloc/free, поэтому существует оператор new/delete .

18. volatile и внешние ключевые слова

Волатильность трех характеристик volatile
: Отражается на уровне сборки, есть два оператора Следующий оператор не использует напрямую содержимое регистра volatile переменной, соответствующей предыдущему оператору , а снова считывает его из памяти.

Неоптимизируемый : volatile говорит компилятору не выполнять различные агрессивные оптимизации моей переменной или даже удалять переменную напрямую, гарантируяИнструкции, записанные программистом в коде, должны выполняться.
Последовательность: последовательность между volatile переменными может быть гарантирована, и компилятор не будет выполнять оптимизацию не по порядку.
внешний

В языке C модификатор extern используется перед объявлением переменной или функции, чтобы указать, что « эта переменная/функция определена в другом месте
и на нее следует ссылаться здесь».

Обратите внимание, что положение объявления extern также связано с его областью действия: если он объявлен в основной функции, его можно вызывать только в основной функции и нельзя вызывать в других функциях. На самом деле, чтобы вызывать функции и переменные в других файлах, вам нужно только включить файл с помощью #include, зачем использовать extern? Потому что использование extern ускорит процесс компиляции программы, что может сэкономить время.

В C++ у extern есть еще одна функция, которая используется для указания спецификации вызова функций C или C++ . Например, чтобы вызвать функцию библиотеки C в C++, вам нужно использовать extern "C", чтобы объявить функцию, на которую будет ссылаться программа C++. Это делается для того, чтобы компоновщик сообщил компоновщику, что при компоновке необходимо выполнить компоновку со спецификацией функции C. Основная причина в том, что программы на C++ и C имеют разные правила именования в целевом коде после компиляции, что используется для решения проблемы сопоставления имен.

19. Разница между define и const (этап компиляции, безопасность, использование памяти и т. д.)

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

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

20. Рассчитайте размер следующих классов

class A{
    
    }; sizeof(A) = 1;//孔磊在实例化时得到一个独一无二的地址,所以为1.
class A{
    
    virtual Fun(){
    
    }}; sizeof(A) = 4(32bit)/8(64bit)//当c++中有虚函数的时候,会有一个指向虚函数表的指针(vptr)
class A{
    
    static int a;}; sizeof(A)=1;
class A{
    
    int a;}; sizeof(A) = 4;
class A{
    
    static int a; int b;}; sizeof(A) = 4;

21. Три основные характеристики объектно-ориентированного подхода и приведите примеры

Три основные особенности объектно-ориентированного языка C++: инкапсуляция, наследование и полиморфизм.
Так называемая инкапсуляция
предназначена для инкапсуляции объективных вещей в абстрактные классы, а классы могут позволять только доверенным классам или объектам работать со своими собственными данными и методами и скрывать ненадежные. Класс — это логическая сущность, которая инкапсулирует данные и код для управления этими данными. Внутри объекта некоторый код или некоторые данные могут быть закрытыми и недоступными для внешнего мира.Таким образом, внутренние данные объекта обеспечивают различные уровни защиты для предотвращения изменений, кроме несущественной части информации в программе. Или неправильно использовать приватную часть объекта.

Так называемое наследование
относится к методу, позволяющему объекту одного типа получать свойства объекта другого типа. Он поддерживает концепцию классификации по уровням. Наследование относится к возможности использовать все функциональные возможности существующего класса и расширять его, не переписывая исходный класс. Новый класс, созданный путем наследования, называется «подклассом» или «производным классом», а унаследованный класс называется «базовым классом», «родительским классом» или «суперклассом». Процесс наследования – это процесс от общего к частному. Чтобы добиться наследования, это может быть достигнуто с помощью «наследования» и «композиции».

Существует два типа реализации концепции наследования:

Наследование реализации : Наследование реализации относится к способности напрямую использовать свойства и методы базового класса без дополнительного кодирования.

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

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

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

Guess you like

Origin blog.csdn.net/qq_43679351/article/details/124931688