Классические вопросы для интервью C++ (наиболее полные, самый высокий процент интервью)

C++

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

2. Разница между удалением и удалением [] delete вызовет деструктор только один раз, тогда как delete[] вызовет деструктор каждого члена. Более подробно это объясняется в разделе «Более эффективный C++»: «Когда оператор удаления используется для массива, он вызывает деструктор для каждого элемента массива, а затем вызывает оператор удаления для освобождения памяти». новый[]

MemTest *mTest1=new MemTest[10];

MemTest *mTest2=new MemTest;

Int *pInt1=new int [10];

Int *pInt2=new int;

delete[]pInt1; //-1-

delete[]pInt2; //-2-

delete[]mTest1;//-3-

delete[]mTest2;//-4-

Сообщить об ошибке на -4-.

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

3. Каковы свойства C++ (объектно-ориентированные функции) Инкапсуляция, наследование и полиморфизм.

4. Следует ли вызывать деструктор родительского класса при уничтожении подкласса? Порядок вызова деструктора таков: сначала деструктор производного класса, а затем деструктор базового класса, то есть при вызове деструктора базового класса информация производного класса полностью уничтожается. При определении объекта сначала вызывается конструктор базового класса, а затем вызывается конструктор производного класса; при деструкции происходит обратное: сначала вызывается деструктор производного класса, а затем вызывается деструктор базового класса.

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

Полиморфизм проявляется в перегрузке функций и операторов во время компиляции;

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

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

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

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

6. Найдите возвращаемое значение следующей функции (Microsoft) целая функция (х)

{

int countx = 0;

while(x)

{

countx ++;

x = x&(x-1);

}

return countx;

}

Предположим, что х = 9999. Ответ: 8

Идея: преобразовать x в двоичное число и посмотреть, сколько единиц оно содержит.

7. Что такое «ссылка»? На какие вопросы следует обратить внимание при объявлении и использовании «референса»? Ответ: Ссылка — это «псевдоним» целевой переменной, и работа приложения точно такая же, как и прямая работа с переменной. При объявлении ссылки не забудьте ее инициализировать. После того, как ссылка объявлена, она эквивалентна имени целевой переменной, имеющему два имени, а именно исходное имя цели и имя ссылки.Имя ссылки не может использоваться в качестве псевдонима для других имен переменных. Объявление ссылки не означает, что переменная определена заново, это означает только то, что имя ссылки является псевдонимом имени целевой переменной.Это не сам тип данных, поэтому сама ссылка не занимает единицу хранения, и система не выделяет единицу хранения для ссылки. Ссылка на массив не может быть создана.

8. Каковы характеристики использования «ссылки» в качестве параметра функции? (1) Передача ссылки на функцию имеет тот же эффект, что и передача указателя. В это время формальный параметр вызываемой функции используется как псевдоним переменной фактического параметра или объекта в исходной вызывающей функции, поэтому операция переменной формального параметра в вызываемой функции относится к соответствующему целевому объекту (в основная функция) в вызывающей функции).

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

(3) Хотя использование указателя в качестве параметра функции также может дать тот же эффект, что и использование ссылки, в вызываемой функции единица хранения также должна быть выделена для формального параметра, а форма «* имя переменной указателя " необходимо использовать повторно для операций. Это подвержено ошибкам, и программа менее читабельна; с другой стороны, в месте вызова вызывающей функции адрес переменной должен использоваться в качестве аргумента. И цитаты проще в использовании и понятнее.

9. Когда вам нужно использовать «постоянные ссылки»? Если вы хотите использовать ссылки для повышения эффективности вашей программы, а также для защиты данных, передаваемых в функцию, от изменения в функции, вы должны использовать константные ссылки. Метод объявления постоянной ссылки: идентификатор константного типа и имя ссылки = имя целевой переменной;

пример 1

int a ;

const int &ra=a;

ра=1;//ошибка

а=1;//правильно

Пример 2

string foo( );

void bar(string & s);

Тогда следующее выражение будет недопустимым:

bar(foo( ));

bar("hello world");

Причина в том, что и foo(), и строка "hello world" создают временный объект, а в C++ эти временные объекты имеют тип const. Таким образом, приведенное выше выражение является попыткой преобразовать объект типа const в неконстантный тип, что является недопустимым. Ссылочные параметры должны быть определены как константы, насколько это возможно, если они могут быть определены как константы.

10. Каковы форматы, преимущества и правила использования «ссылки» в качестве типа возвращаемого значения функции?

Формат: идентификатор типа и имя функции (список параметров и описание типа) { // тело функции }

Преимущество: Копия возвращаемого значения не делается в памяти; (Примечание: Именно по этой причине не рекомендуется возвращать ссылку на локальную переменную. Потому что с окончанием времени жизни локальной переменной соответствующая ссылка также потерпит неудачу, что приведет к ошибке времени выполнения!

Меры предосторожности:

(1) Ссылка на локальную переменную не может быть возвращена. Это может относиться к пункту 31 Эффективного C++[1]. Основная причина в том, что локальные переменные будут уничтожены после возврата из функции, поэтому возвращаемая ссылка становится ссылкой «ничего», и программа переходит в неизвестное состояние.

(2) Ссылка на память, выделенную new внутри функции, не может быть возвращена. Это может относиться к пункту 31 Эффективного C++[1]. Хотя пассивного уничтожения локальных переменных нет, для этой ситуации (возврат ссылки на память, выделенную new внутри функции) есть другие неловкие ситуации. Например, ссылка, возвращаемая функцией, отображается только как временная переменная без присвоения фактической переменной, тогда пространство, на которое указывает ссылка (выделенное new), не может быть освобождено, что приводит к утечке памяти.

(3) Ссылки на члены класса могут быть возвращены, но желательно, чтобы они были константными. Этот принцип может относиться к пункту 30 Эффективного C++ [1]. Основная причина в том, что когда свойство объекта связано с бизнес-правилом, его назначение часто связано с какими-то другими свойствами или состоянием объекта, поэтому необходимо инкапсулировать операцию присвоения в бизнес-правило. Если другие объекты могут получить неконстантную ссылку (или указатель) на свойство, то простое присвоение свойства нарушит целостность бизнес-правил.

(4) Возвращаемое значение перегрузки оператора потока объявляется как «ссылка»:

Потоковые операторы >, эти два оператора часто используются последовательно, например: cout

Оператор присваивания =. Этот оператор, как и оператор потока, можно использовать постоянно, например: x = j = 10 или (x = 10) = 100; Возвращаемое значение оператора присваивания должно быть lvalue, чтобы его можно было непрерывно присваивать. Поэтому ссылка становится единственным вариантом возвращаемого значения для этого оператора.

#include

int &put(int n);

int vals[10];

int error=-1;

void main()

{

put(0)=10;//Используем значение функции put(0) в качестве lvalue, что эквивалентно vals[0]=10;

put(9)=20;//Используем значение функции put(9) в качестве lvalue, что эквивалентно vals[9]=20;

cout<<vals[0];

cout<<vals[9];

}

int &put(int n)

{

if (n>=0 && n<=9 ) return vals[n];

else { cout<<"subscript error"; return error; }

}

(5) В некоторых других операторах, но не должен возвращать ссылку: +-*/ четыре оператора. Они не могут возвращать ссылки, что подробно обсуждается в статье 23 книги Effective C++ [1]. Основная причина заключается в том, что эти четыре оператора не имеют побочных эффектов, поэтому они должны создавать объект в качестве возвращаемого значения.Дополнительные решения включают в себя: возврат объекта, возврат ссылки на локальную переменную, возврат ссылки на объект, выделенный оператором new. , возвращая ссылку на статический объект. Оба варианта 2 и 3 отклоняются в соответствии с тремя упомянутыми выше правилами для ссылки в качестве возвращаемого значения. Ссылки на статические объекты снова вызывают ошибки, потому что ((a+b) == (c+d)) всегда будет истинным. Таким образом, остается только один вариант — вернуть объект.

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

(2) Для назначения разных членов объединения другие члены будут переписаны, значение исходного члена не существует, а присвоение разных членов структуры не влияет друг на друга.

12. Попробуйте написать результат программы: интервал а=4;

int &f(int x)

{ a=a+x;

return a;

}

int main(void)

{ int t=5;

cout<<f(t)<<endl; a = 9

f(t)=20; a = 20

cout<<f(t)<<endl; t = 5,a = 20 a = 25

t=f(t); a = 30 t = 30

cout<<f(t)<<endl; } t = 60

}

13. В чем разница между перегрузкой и переопределением? Часто задаваемые вопросы. По определению:

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

Переопределение: относится к методу, в котором подкласс переопределяет виртуальную функцию суперкласса.

По принципу реализации:

Перегрузка: компилятор изменяет имена функций с одинаковыми именами в соответствии с разными списками параметров функций, после чего эти функции с одинаковыми именами становятся разными функциями (по крайней мере, для компилятора). Например, есть две функции с одинаковыми именами: function func(p:integer):integer и function func(p:string):integer;. Тогда измененные компилятором имена функций могут быть такими: int_func, str_func. Вызовы этих двух функций определяются между компиляторами и являются статическими. То есть их адреса привязываются во время компиляции (раннее связывание), так что перегрузка не имеет ничего общего с полиморфизмом!

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

14. В каких случаях вместо присваивания можно использовать только список инициализации? Ответ: Когда класс содержит константные и ссылочные переменные-члены, конструктор базового класса должен инициализировать таблицу.

  1. Безопасен ли тип С++? Ответ: нет. Можно выполнять приведение (с повторным приведением) между двумя указателями разных типов. C# является типобезопасным.

  2. Какой код будет выполняться перед выполнением основной функции? Ответ: Конструктор глобального объекта выполняется перед основной функцией.

  3. Опишите, как распределяется память и чем они отличаются? 1) Выделить из области статического хранения. Память выделяется при компиляции программы, и эта память существует на все время выполнения программы. Например, глобальные переменные, статические переменные.

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

3) Выделить из кучи, также известное как динамическое выделение памяти. Когда программа работает, используйте malloc или new для применения любого объема памяти, и программист несет ответственность за то, когда использовать free или delete для освобождения памяти. Срок жизни динамической памяти определяется программистом, она очень гибкая в использовании, но также имеет больше всего проблем.

18. Запишите оператор сравнения между переменной a типа BOOL, int, float и типа указателя и "ноль" соответственно. Отвечать:

BOOL : if ( !a ) or if(a)

int : if ( a == 0)

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

pointer : if ( a != NULL) or if(a == NULL)

19. Назовите, пожалуйста, преимущества const по сравнению с #define? Отвечать:

Роль const: определение констант, изменение параметров функции и изменение возвращаемых значений функции. Все, что изменено Const, применяется для предотвращения случайных изменений и повышения надежности программы.

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

2) Некоторые встроенные инструменты отладки могут отлаживать константы-константы, но не могут отлаживать макро-константы.

20. Кратко опишите разницу между массивом и указателем? Массивы создаются либо в статическом хранилище (например, глобальные массивы), либо в стеке. Указатель может указывать на любой тип блока памяти в любое время.

(1) Различия в пересмотренном содержании

символ а[] = "привет";

а[0] = 'Х';

char *p = "world"; // обратите внимание, что p указывает на константную строку

p[0] = 'X'; // компилятор не может найти эту ошибку, ошибка времени выполнения

(2) Емкость (количество байтов) массива можно вычислить с помощью оператора sizeof. sizeof(p), p получает количество байтов переменной-указателя для указателя, а не объем памяти, на который указывает p. Язык C++/C не имеет возможности узнать объем памяти, на который указывает указатель, если он не запоминается при выделении памяти. Обратите внимание, что когда массив передается в качестве параметра функции, массив автоматически вырождается в указатель того же типа.

char a[] = "hello world";

char *p = a;

cout

cout

Вычислить объем памяти массивов и указателей

void Func(char a[100])

{

cout

}

Вопрос 21: Что представляет собой int (*s[10])(int)? int (*s[10])(int) Массив указателей на функции, каждый указатель указывает на функцию от int func(int param).

Вопрос 22: Память стека и литеральная константная область char str1[] = "abc";

char str2[] = "abc";

const char str3[] = "abc";

const char str4[] = "abc";

const char *str5 = "abc";

const char *str6 = "abc";

char *str7 = "abc";

char *str8 = "abc";

cout

cout

cout

cout

Результат: 0 0 1 1

Ответ: str1, str2, str3, str4 — это переменные-массивы, они имеют свое собственное пространство памяти, а str5, str6, str7, str8 — указатели, они указывают на одну и ту же константную область.

Вопрос 23: Переместить программу на указанный адрес памяти Чтобы присвоить значение абсолютному адресу 0x100000, мы можем использовать (unsigned int*) 0x100000 = 1234; тогда, если мы хотим, чтобы программа выполнялась по абсолютному адресу 0x100000, что нам делать?

((void ()( ))0x100000 ) ( );

Сначала приведите 0x100000 к указателю на функцию, т.е.:

(void (*)())0x100000

Затем вызовите его снова:

((void ()())0x100000)();

Использование typedef может быть более интуитивным:

typedef void(*)() voidFuncPtr;

*((voidFuncPtr)0x100000)();

Вопрос 24: int id[sizeof(unsigned long)]; правильно ли это? Зачем? Ответ: Верно. Этот sizeof является оператором времени компиляции, который определяется во время компиляции и может рассматриваться как машинная константа.

25 Вопрос: В чем разница между ссылкой и указателем? 【Справочный ответ】

  1. Ссылки должны быть инициализированы, а указатели — нет.

  2. Ссылка не может быть изменена после ее инициализации, указатель может изменить объект, на который он указывает.

  3. Нет ссылок на нули, но есть указатели на нули.

Вопрос 26: Сравнение const и #define, в чем преимущества const? 【Справочный ответ】

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

(2) Некоторые встроенные средства отладки могут отлаживать константы-константы, но не могут отлаживать макро-константы.

Вопрос 27: Сложные операторы

void * ( * (*fp1)(int))[10];

float (( fp2)(int,int,int))(int);

int (* ( * fp3)())10;

Что они имеют в виду?

【стандартный ответ】

1.void * ( * (fp1)(int))[10]; fp1 — указатель, указывающий на функцию, параметр этой функции — тип int, возвращаемое значение функции — указатель, этот указатель указывает на массив, этот массив имеет 10 элементы, каждый элемент является пустотойуказатель типа.

2.float ((fp2)(int,int,int))(int); fp2 — указатель на функцию, параметры этой функции — 3 типа int, возвращаемое значение функции — указатель, этот указатель указывает на функцию, Параметр функции имеет тип int, а возвращаемое значение функции имеет тип float.

3.int (* ( * fp3)())10; fp3 — указатель на функцию, параметр этой функции пустой, возвращаемое значение функции — указатель, этот указатель указывает на массив, в этом массиве 10 элементов, каждый элемент — указатель, указывает на функцию , this Параметр функции пуст, а возвращаемое значение функции имеет тип int.

Вопрос 28. Сколькими способами распределяется память? 【Справочный ответ】

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

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

В-третьих, выделение из кучи, также известное как динамическое выделение памяти. Когда программа работает, используйте malloc или new для применения любого объема памяти, и программист несет ответственность за то, когда использовать free или delete для освобождения памяти. Время жизни динамической памяти зависит от нас, она очень гибкая в использовании, но и имеет больше всего проблем.

Вопрос 29: Деструктор базового класса не является виртуальной функцией, какие проблемы он принесет? [Справочный ответ] Не используется деструктор производного класса, что приведет к утечке ресурсов.

30 Вопрос: В чем разница между глобальными и локальными переменными? Как это достигается? Как операционные системы и компиляторы узнают об этом? 【Справочный ответ】

Жизненный цикл отличается:

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

Различные способы использования: после объявления глобальные переменные могут использоваться всеми частями программы; локальные переменные могут использоваться только локально; размещаются в области стека.

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

Люди должны полагаться на себя~