50208 (572508), страница 2
Текст из файла (страница 2)
Краткое описание:
Это статья про хакеров...
Если же мы напишем вот так:
function Add_article($author="Иванов Иван",
$title, $description){
// ...действия как в предыдущем примере
}
Add_article("Кто такие хакеры",
"Это статья про хакеров...");
?>
То в результате получим:
Warning: Missing argument 3 for
add_article() in
c:\users\nina\tasks\func\def_bad.php
on line 2
Возвращаемые значения
Все функции, приведенные выше в качестве примеров, выполняли какие-либо действия. Кроме подобных действий, любая функция может возвращать как результат своей работы какое-нибудь значение. Это делается с помощью утверждения return. Возвращаемое значение может быть любого типа, включая списки и объекты. Когда интерпретатор встречает команду return в теле функции, он немедленно прекращает ее исполнение и переходит на ту строку, из которой была вызвана функция.
Например, составим функцию, которая возвращает возраст человека. Если человек не умер, то возраст считается относительно текущего года.
/* если второй параметр вычисляется
как true, то он рассматривается как
дата смерти, */
function Age($birth, $is_dead){
if ($is_dead) return $is_dead-$birth;
else return date("Y")-$birth;
}
echo Age(1971, false); // выведет 33
echo Age(1971, 2001); // выведет 30
?>
В этом пример можно было и не использовать функцию return, а просто заменить ее функцией вывода echo. Однако если мы все же делаем так, что функция возвращает какое-то значение (в данном случае возраст человека), то в программе мы можем присвоить любой переменной значение этой функции: $my_age = Age(1981, 2004);
В результате работы функции может быть возвращено только одно значение. Несколько значений можно получить, если возвращать список значений (одномерный массив). Допустим, мы хотим получить полный возраст человека с точностью до дня.
function Full_age($b_day, $b_month, $b_year){
if (date("m")>$b_month && date("d")>$b_day)
{
$day = date ("d") - $b_day;
$month = date("m") - $b_month;
$year = date("Y") - $b_year;
} else {
$year = date("Y") - $b_year - 1;
$day = 31 - ($b_day - date ("d"));
$month = 12 - ($b_month - date("m"));
}
return array ($day,$month,$year);
}
$age = Full_age("07","08","1974");
echo "Вам $age[2] лет, $age[1] месяцев
и $age[0] дней";
// выведет "Вам 29 лет, 11 месяцев и 5 дней"
?>
Когда функция возвращает несколько значений для их обработки в программе, удобно использовать языковую конструкцию list(), которая позволяет одним действием присвоить значения сразу нескольким переменным. Например, в предыдущем примере, оставив без изменения функцию, обработать возвращаемые ей значения можно было так:
// задание функции Full_age()
list($day,$month,$year) = Full_age("07",
"08","1974");
echo "Вам $year лет, $month месяцев и
$day дней";
?>
Вообще конструкцию list() можно использовать для присвоения переменным значений элементов любого массива.
$arr = array("first","second");
list($a,$b) = $arr;
// переменной $a присваивается первое
// значение массива, $b – второе
echo $a," ",$b;
// выведет строку «first second»
?>
Пример 5.9. Использование list()
Возвращение ссылки
В результате своей работы функция также может возвращать ссылку на какую-либо переменную. Это может пригодиться, если требуется использовать функцию для того, чтобы определить, какой переменной должна быть присвоена ссылка. Чтобы получить из функции ссылку, нужно при объявлении перед ее именем написать знак амперсанд (&) и каждый раз при вызове функции перед ее именем тоже писать амперсанд (&). Обычно функция возвращает ссылку на какую-либо глобальную переменную (или ее часть – ссылку на элемент глобального массива), ссылку на статическую переменную (или ее часть) или ссылку на один из аргументов, если он был также передан по ссылке.
$a = 3; $b = 2;
function & ref($par){
global $a, $b;
if ($par % 2 == 0) return $b;
else return $a;
}
$var =& ref(4);
echo $var, " и ", $b, "
";
//выведет 2 и 2
$b = 10;
echo $var, " и ", $b, "
";
// выведет 10 и 10
?>
Пример 5.10. Возвращение ссылки
При использовании синтаксиса ссылок в переменную $var нашего примера не копируется значение переменной $b возвращенной функцией $ref, а создается ссылка на эту переменную. То есть теперь переменные $var и $b идентичны и будут изменяться одновременно.
6. Что такое класс? В чем отличие класса от структуры? Приведите примеры структуры и класса
Для С++ классы System.Object, System.Exception, System.File-Stream и System.Random — это ссылочные типы (память выделяется из упр. кучи). В свою очередь размерные типы в документации называются структурами (structure) и перечислениями (enumeration). Например, структуры System.In132, System.Boolean, System.Decimal, System.TimeSpan и перечисления System.DayOfWeek, System.10.FileAttributes и System.Drawing.FontStyle являются размерными типами (хранятся обычно в стеке потока, но могут быть встроены в ссылочные типы).
Переменные размерного типа непосредственно содержат данные, а переменные ссылочного типа содержат ссылку на область памяти, содержащую данные. При этом, память под ссылочные переменные всегда выделяется в куче, а память под размерные, обычно, в стеке. Как известно, в .NET для освобождения неиспользуемой памяти в куче используется сборщик мусора. Особенностью стека является то, что память в нем освобождается автоматически (без каких-либо накладных расходов). Таким образом, уничтожение ссылочных объектов путем сборки мусора менее эффективно, чем размерных.
Другой важный момент - это упаковка (boxing) и распаковка (unboxing). При упаковке размерного типа происходит выделение области памяти в куче и копирование значения размерного типа в эту область. Упакованный размерный тип обладает свойствами ссылочного. Распаковка - обратный процесс, в результате которого упакованный размерный тип копируется на стек. Благодаря упаковке любой размерный тип может интерпретироваться как ссылочный и, как следствие этого, любой размерный тип может использоваться вместо object. Важно понимать, что упаковка и распаковка требуют дополнительных затрат памяти и времени. Поэтому следует избегать этих операций в большом количестве.
Теперь вернемся к нашему вопросу. Отличие структуры от класса:
- Структура является размерным типом, а класс - ссылочным.
- Все структурные типы неявно наследуются от System.ValueType, они не бывают абстрактными и всегда неявно запечатаны (sealed)
- При присваивании переменных структурного типа, создается копия данных
- Объявления полей структуры не могут иметь инициализаторов
- Различная интерпретация this для структуры и класса
- Структура не может содержать конструктор без параметров
- Структура не может содержать деструктор
- Для ссылочных типов значение по умолчанию - null
- При конвертировании между ссылочным и размерным типами происходит упаковка и распаковка
Главное помнить и понимать главные:
1 - следствием этого является то, что экземпляр класса создается в куче, а структуры, обычно (но не всегда) на стеке
3 - это должно быть очевидно
4- является следствием 6, т.к. код инициализации полей неявно вставляется во все конструкторы
6 является следствием оптимизации использования структур по скорости
Особенности 8 для структур и 9 рассмотрим ниже более подробно, т.к. именно на них любят акцентировать внимание на собеседовании.
Рассмотрим такой пример:
1.public struct MyStruct
2.{
3. public int m1;
4. public string s1;
5.}
6....
7.MyStruct ms1;
8.MyStruct ms2 = new MyStruct();
9.
10.Console.WriteLine(ms1.m1);
11.Console.WriteLine(ms2.m1);
12....
13.
В чем отличие между объявлениями в строках 7 и 8? Некоторые считают, что в первом случае (строка 7) объект создается на стеке, а во втором (строка 8) происходит упаковка и объект создается в куче. На самом деле это не так. В обоих случаях объект создается на стеке. Разница в том, что в строке 7 будет создан неинициализированный объект, а в строке 8 инициализированный. Поля ms2 будут содержать значения по умолчанию (0 для m1 и null для s1), а поля ms1 неопределены. Поэтому в строке 10 возникнет ошибка компиляции.
Теперь рассмотрим нюансы, связанные с упаковкой и распаковкой. Как известно, размерные типы могут наследоваться от интерфейсов (имплиментировать интерфейсы). Часто спрашивают, будет ли производиться упаковка при приведении размерного типа к интерфейсу. Правильный ответ - да, будет, т.к. интерфейс является ссылочным типом.
Рассмотрим пример:
1.int i = 1;
2.Console.WriteLine(i.ToString());
3.Console.WriteLine(((IFormattable)i).ToString());
4.Console.WriteLine("{0}", i);
5.
Размерный тип int имплиментирует интерфейс IFormattable, содержащий метод ToString(). Так как метод ToString() является частью класса int, а компилятор знает, что это размерный тип и, следовательно, виртуальный метод ToString() не может быть переопределен (т.к. структурный тип является запечатанным), компилятор вставляет непосредственный вызов метода в строку 2 и упаковки не происходит. В строке 3 происходит упаковка, т.к. i приводится к интерфейсу IFormattable. Теперь вы сами можете сказать, что происходит в строке 4: неявное приведение к интерфейсу IFormattable и вызов метода ToString(), что, также, приводит к упаковке.
И еще один момент. Массивы являются ссылочными типами, но могут содержать размерные. Где же будет размещен, например, массив целых чисел? В куче, причем целые числа будут неупакованными.
Значением this. Для класса:
class Indirect
{
//...
public void Method(Indirect that)
{ RefParameter(ref this); // compile-time error
OutParameter(out this); // compile-time error
this = that; // compile-time error
}
//...
}
Для структуры:
struct Direct
{
//...
public void Reassign(Direct that)
{
RefParameter(ref this); // compiles ok
OutParameter(out this); // compiles ok
this = that; // compiles ok
}
//...
}
Структура не может быть null, и вот это не пройдет:
if (s == null) ... // compile-time error, where s - struct
Структуру не можно использовать с оператором as
Direct no = s as Direct; // compile-time error, where Direct - struct
Структуру не можно использовать с оператором lock
lock(s) { ... } // compile-time error, where s - struct
Не может иметь полей типа volatile (Ключевое слово volatile указывает, что поле может быть изменено несколькими потоками, выполняющимися одновременно.)
private volatile Direct field; // compile-time error, where Direct - struct
Только структру могу работать с указателями, примеры
(Ключевое слово unsafe обозначает небезопасный контекст, необходимый для работы с указателями.)
Direct variable = new Direct();
unsafe {
Direct * ptr = &variable; // compiles ok
//...
}
но с классом
Indirect variable = new Indirect();
unsafe {
fixed(Indirect * ptr = &variable) // compile-time error
{
//...
}
}
а так же
Direct * array = stackalloc Direct[42]; // compiles ok
Только структуры могу использовать sizeof
int size = sizeof(Direct); // compiles ok
По разному работает сравнение Equals
struct Direct
{
public Direct(int value)
{
field = value;
}
private int field;
}
class Indirect
{
public Indirect(int value)
{
field = value;
}
private int field;
}
class EqualsBehavior
{
static void Main()
{
Direct s1 = new Direct(42);
Direct s2 = new Direct(42);
Indirect c1 = new Indirect(42);
Indirect c2 = new Indirect(42);
bool structEquality = s1.Equals(s2); // true
bool classIdentity = c1.Equals(c2); // false
}
}
7. Что такое виртуальная функция? Приведите пример. В каких случаях используются виртуальные функции?
В случае переопределяемых функций компилятор умеет отличать один вызов от другого по типу их аргументов. Используя эту информацию, он "жестко" связывает коды программы с соответствующими функциями элементами. С другой стороны, бывает необходимо отличить один вызов от другого, при наличии аргументов одного типа на этапе выполнения, и обеспечить потомки класса разными версиями функций базового класса. Именно использование ключевого слова virtual приводит к отсрочке связывания, и вызову нужной функции на этапе выполнения.
Виртуальная функция элемент - это функция, которая будучи описана в потомках, замещает собой соответствующую функцию элемент везде - даже в предке, если она вызывается для потомка. В отличии от раннего связывания с использованием переопределяемых функций элементов, виртуальные функции элементы должны иметь аргументы одного типа.
Синтаксис определения виртуальных функций элементов очень прозрачный: добавьте слово virtual к первому определению функции элементу: virtual void Show(); virtual void Hide();
Только встроенные функции элементы могут быть объявлены как виртуальные. Как только функция объявлена виртуальной, она не может быть переопределена ни в каком наследуемом классе с однотипным перечнем аргументов, но с другим типом возвращаемого значения. Если вы переопределяете Show с тем же перечнем однотипных аргументов и таким же типом возвращаемого значения, то новая функция Show автоматически становится виртуальной, независимо от того, используется ключевое слово virtual или нет. В этом случае говорят, что новая виртуальная Show замещает Show в своем базовом классе. Вы можете свободно переопределять Show с другим перечнем разнотипных аргументов (изменяя при этом тип возвращаемого значения или нет), но виртуальный механизм не задействуется для такой версии Show. акая именно функция элемент Show будет вызвана - зависит только от класса объекта, для которого вызывается Show, даже если вызов производится через указатель на базовый класс. Например,
Circle ACircle
Point* APoint_рointer = &ACircle; // указатель на Circle,
// которому присваивается
// значение указателя на
// базовый класс, Point
APoint_рointer->Show(); // вызывает Circle::Show!