Использование переменного числа аргументов 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Использование переменного числа аргументов



При создании метода обычно заранее известно число аргументов, которые будут переданы ему, но так бывает не всегда. Иногда возникает потребность создать метод, которому можно было бы передать произвольное число аргументов. Допустим, что требуется метод, обнаруживающий наименьшее среди ряда значений. Такому методу можно было бы передать не менее двух, трех, четырех или еще больше значений. Но в любом случае метод должен возвратить наименьшее из этих значений. Такой метод нельзя создать, используя обычные параметры. Вместо этого придется воспользовать­ся специальным типом параметра, обозначающим произвольное число параметров. И это делается с помощью создаваемого параметра типа params.

Для объявления массива параметров, способного принимать от нуля до нескольких аргументов, служит модификатор params. Число элементов массива параметров бу­дет равно числу аргументов, передаваемых методу. А для получения аргументов в про­грамме организуется доступ к данному массиву.

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

Листинг 8.12

// Продемонстрировать применение модификатора params.

 

using System;

 

class Min

{

public int MinVal(params int[] nums)

{

int m;

 

if(nums.Length == 0) {

Console.WriteLine("Ошибка: нет аргументов.");

return 0;

}

 

m = nums[0];

for(int i=1; i < nums.Length; i++)

if(nums[i] < m) m = nums[i];

 

return m;

}

}

 

class ParamsDemo

{

static void Main()

{

Min ob = new Min();

int min;

int a = 10, b = 20;

 

// Вызвать метод с двумя значениями.

min = ob.MinVal(a, b);

Console.WriteLine("Наименьшее значение равно " + min);

 

// Вызвать метод с тремя значениями.

min = ob.MinVal(a, b, -1);

Console.WriteLine("Наименьшее значение равно " + min);

 

// Вызвать метод с пятью значениями.

min = ob.MinVal(18, 23, 3, 14, 25);

Console.WriteLine("Наименьшее значение равно " + min);

 

// Вызвать метод с массивом целых значений.

int[] args = { 45, 67, 34, 9, 112, 8 };

min = ob.MinVal(args);

Console.WriteLine("Наименьшее значение равно " + min);

}

}

При выполнении этой программы получается следующий результат.

Наименьшее значение равно 10

Наименьшее значение равно -1

Наименьшее значение равно 3

Наименьшее значение равно 8

Всякий раз, когда вызывается метод MinVal(), ему передаются аргументы в мас­сиве nums. Длина этого массива равна числу передаваемых аргументов. Поэтому с по­мощью метода MinVal() можно обнаружить наименьшее среди любого числа зна­чений.

Обратите внимание на последний вызов метода MinVal(). Вместо отдельных значе­ний в данном случае передается массив, содержащий ряд значений. И такая передача аргументов вполне допустима. Когда создается параметр типа params, он восприни­мает список аргументов переменной длины или же массив, содержащий аргументы.

Несмотря на то что параметру типа params может быть передано любое число аргументов, все они должны иметь тип массива, указываемый этим параметром. Например, вызов метода MinVal()

min = ob.MinVal(1, 2.2); // Неверно!

считается недопустимым, поскольку нельзя автоматически преобразовать тип double (значение 2.2) в тип int, указанный для массива nums в методе MinVal().

Пользоваться модификатором params следует осторожно, соблюдая граничные условия, так как параметр типа params может принимать любое число аргументов - даже нулевое! Например, вызов метода MinVal() в приведенном ниже фрагменте кода считается правильным с точки зрения синтаксиса С#.

min = ob.MinVal(); // нет аргументов

min = ob.MinVal(3); // 1 аргумент

Именно поэтому в методе MinVal() организована проверка на наличие в масси­ве nums хотя бы одного элемента перед тем, как пытаться получить доступ к этому элементу. Если бы такой проверки не было, то при вызове метода MinVal() без аргу­ментов возникла бы исключительная ситуация во время выполнения. Больше того, код метода MinVal() написан таким образом, чтобы его можно было вызывать с одним аргументом. В этом случае возвращается этот единственный аргумент.

У метода могут быть как обычные параметры, так и параметр переменной дли­ны. В качестве примера ниже приведена программа, в которой метод ShowArgs()принимает один параметр типа string, а также целочисленный массив в качестве параметра типа params.

Листинг 8.13

// Использовать обычный параметр вместе с параметром

// переменной длины типа params.

 

using System;

 

class MyClass

{

public void ShowArgs(string msg, params int[] nums)

{

Console.Write(msg + ": ");

 

foreach(int i in nums)

Console.Write(i + " ");

 

Console.WriteLine();

}

}

 

class ParamsDemo2 {

static void Main() {

MyClass ob = new MyClass();

 

ob.ShowArgs("Это ряд целых чисел",

1, 2, 3, 4, 5);

 

ob.ShowArgs("А это еще два целых числа",

17, 20);

}

}

Вот какой результат дает выполнение этой программы.

Это ряд целых чисел: 1, 2, 3, 4, 5

А это еще два целых числа: 17, 20

В тех случаях, когда у метода имеются обычные параметры, а также параметр пере­менной длины типа params, он должен быть указан последним в списке параметров данного метода. Но в любом случае параметр типа params должен быть единственным.

Возврат объектов из методов

Метод может возвратить данные любого типа, в том числе и тип класса. Ниже в ка­честве примера приведен вариант класса Rect, содержащий метод Enlarge(), в ко­тором строится прямоугольник с теми же сторонами, что и у вызывающего объекта прямоугольника, но пропорционально увеличенными на указанный коэффициент.

Листинг 8.14

// Возврат объекта из метода.

 

using System;

 

class Rect

{

int width;

int height;

 

public Rect(int w, int h)

{

width = w;

height = h;

}

 

public int Area()

{

return width * height;

}

 

public void Show()

{

Console.WriteLine(width + " " + height);

}

 

/* Метод возвращает прямоугольник со сторонами, пропорционально

увеличенными на указанный коэффициент по сравнению с

вызывающим объектом прямоугольника. */

public Rect Enlarge(int factor)

{

return new Rect(width * factor, height * factor);

}

}

 

class RetObj

{

static void Main()

{

Rect r1 = new Rect(4, 5);

 

Console.Write("Размеры прямоугольника r1: ");

r1.Show();

Console.WriteLine("Площадь прямоугольника r1: " + r1.Area());

 

Console.WriteLine();

 

// Создать прямоугольник в два раза больше прямоугольника r1.

Rect r2 = r1.Enlarge(2);

 

Console.Write("Размеры прямоугольника r2: ");

r2.Show();

Console.WriteLine("Площадь прямоугольника r2 " + r2.Area());

}

}

Выполнение этой программы дает следующий результат.

Размеры прямоугольника rl: 4 5

Площадь прямоугольника rl: 20

Размеры прямоугольника г2: 8 10

Площадь прямоугольника г2: 80

Когда метод возвращает объект, последний продолжает существовать до тех пор, пока не останется ссылок на него. После этого он подлежит сборке как "мусор". Сле­довательно, объект не уничтожается только потому, что завершается создавший его метод.

Одним из практических примеров применения возвращаемых данных типа объ­ектов служит фабрика класса, которая представляет собой метод, предназначенный для построения объектов его же класса. В ряде случаев предоставлять пользователям класса доступ к его конструктору нежелательно из соображений безопасности или же потому, что построение объекта зависит от некоторых внешних факторов. В подобных случаях для построения объектов используется фабрика класса. Обратимся к просто­му примеру.

Листинг 8.15

// Использовать фабрику класса.

 

using System;

 

class MyClass

{

int a, b; // закрытые члены класса

 

// Создать фабрику для класса MyClass.

public MyClass Factory(int i, int j)

{

MyClass t = new MyClass();

 

t.a = i;

t.b = j;

 

return t; // возвратить объект

}

 

public void Show()

{

Console.WriteLine("a и b: " + a + " " + b);

}

 

}

 

class MakeObjects {

static void Main() {

MyClass ob = new MyClass();

int i, j;

 

// Сформировать объекты, используя фабрику классов.

for(i=0, j=10; i < 10; i++, j--) {

MyClass anotherOb = ob.Factory(i, j); // создать объект

anotherOb.Show();

}

 

Console.WriteLine();

}

}

Вот к какому результату приводит выполнение этого кода

a и b: 0 10

a и b: 1 9

a и b: 2 8

a и b: 3 7

a и b: 4 6

a и b: 5 5

a и b: 6 4

a и b: 7 3

a и b: 8 2

a и b: 9 1

Рассмотрим данный пример более подробно. В этом примере конструктор для класса МуСlass не определяется, и поэтому доступен только конструктор, вызывае­мый по умолчанию. Это означает, что значения переменных а и b нельзя задать с по­мощью конструктора. Но в фабрике класса Factory() можно создать объекты, в ко­торых задаются значения переменных а и b. Более того, переменные а и b являются закрытыми, и поэтому их значения могут быть заданы только с помощью фабрики класса Factory().

В методе Main() получается экземпляр объекта класса MyClass, а его фабричный метод используется в цикле for для создания десяти других объектов. Ниже приведе­на строка кода, в которой создаются эти объекты.

MyClass anotherOb = ob.Factory(i, j); // создать объект

На каждом шаге итерации цикла создается переменная ссылки на объект anotherOb, которой присваивается ссылка на объект, формируемый фабрикой клас­са. По завершении каждого шага итерации цикла переменная anotherOb выходит за пределы области своего действия, а объект, на который она ссылается, утилизируется.

Возврат массива из метода

В С# массивы реализованы в виде объектов, а это означает, что метод может также возвратить массив. (В этом отношении С# отличается от С++, где не допускается воз­врат массивов из методов.) В качестве примера ниже приведена программа, в которой метод FindFactors() возвращает массив, содержащий множители переданного ему аргумента.

Листинг 8.16

// Возвратить массив из метода.

 

using System;

 

class Factor

{

/* Метод возвращает массив facts, содержащий множители аргумента

num. При возврате из метода параметр numfactors типа out будет

содержать количество обнаруженных множителей. */

public int[] FindFactors(int num, out int numfactors)

{

int[] facts = new int[80]; // размер массива 80 выбран произвольно

int i, j;

 

// Найти множители и поместить их в массив facts.

for(i=2, j=0; i < num/2 + 1; i++)

if((num%i)==0) {

facts[j] = i;

j++;

}

 

numfactors = j;

return facts;

}

}

 

class FindFactors

{

static void Main()

{

Factor f = new Factor();

int numfactors;

int[] factors;

 

factors = f.FindFactors(1000, out numfactors);

 

Console.WriteLine("Factors for 1000 are: ");

for(int i=0; i < numfactors; i++)

Console.Write(factors[i] + " ");

 

Console.WriteLine();

}

}

При выполнении этой программы получается следующий результат.

Множители числа 1000:

2 4 5 8 10 20 25 40 50 100 125 200 250 500

В классе Factor метод FindFactors () объявляется следующим образом.

public int[] FindFactors(int num, out int numfactors) {

Обратите внимание на то, как указывается возвращаемый массив типа int. Этот синтаксис можно обобщить. Всякий раз, когда метод возвращает массив, он указыва­ется аналогичным образом, но с учетом его типа и размерности. Например, в следую­щей строке кода объявляется метод someMeth (), возвращающий двумерный массив типа double.

public double[,] someMeth() { //...

Перегрузка методов

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

В общем, для перегрузки метода достаточно объявить разные его варианты, а об остальном позаботится компилятор. Но при этом необходимо соблюсти следующее важное условие: тип или число параметров у каждого метода должны быть разными. Совершенно недостаточно, чтобы два метода отличались только типами возвращае­мых значений. Они должны также отличаться типами или числом своих параметров. (Во всяком случае, типы возвращаемых значений дают недостаточно сведений ком­пилятору С#, чтобы решить, какой именно метод следует использовать.) Разумеется, перегружаемые методы могут отличаться и типами возвращаемых значений. Когда вызывается перегружаемый метод, то выполняется тот его вариант, параметры кото­рого соответствуют (по типу и числу) передаваемым аргументам.

Ниже приведен простой пример, демонстрирующий перегрузку методов.

Листинг 8.17

// Продемонстрировать перегрузку методов.

 

using System;

 

class Overload

{

public void OvlDemo() {

Console.WriteLine("Без параметров");

}

 

// Перегрузка метода OvlDemo с одним целочисленным параметром.

public void OvlDemo(int a)

{

Console.WriteLine("Один параметр: " + a);

}

 

// Перегрузка метода OvlDemo с двумя целочисленными параметрами.

public int OvlDemo(int a, int b)

{

Console.WriteLine("Два параметра: " + a + " " + b);

return a + b;

}

 

// Перегрузка метода OvlDemo с двумя параметрами типа double.

public double OvlDemo(double a, double b)

{

Console.WriteLine("Два параметра типа double: " +

a + " "+ b);

return a + b;

}

}

 

class OverloadDemo

{

static void Main()

{

Overload ob = new Overload();

int resI;

double resD;

 

// Вызвать все варианты метода OvlDemo().

ob.OvlDemo();

Console.WriteLine();

 

ob.OvlDemo(2);

Console.WriteLine();

 

resI = ob.OvlDemo(4, 6);

Console.WriteLine("Результат вызова метода ob.OvlDemo(4,6): "

+ resI);

Console.WriteLine();

 

resD = ob.OvlDemo(1.1, 2.32);

Console.WriteLine("Результат вызова метода ob.OvlDemo(1.1,2.32): "

+ resD);

}

}

Вот к какому результату приводит выполнение приведенного выше кода.

Без параметров

Один параметр: 2

Два параметра: 4 6

Результат вызова метода ob.OvlDemo(4, 6): 10

Два параметра типа double: 1.1 2.32

Результат вызова метода ob.OvlDemo(1.1, 2.32): 3.42

Как видите, метод OvlDemo() перегружается четыре раза. Первый его вариант не получает параметров, второй получает один целочисленный параметр, третий - два целочисленных параметра, а четвертый -два параметра типа double. Обратите так­же внимание на то, что два первых варианта метода OvlDemo() возвращают значение типа void, а по существу, не возвращают никакого значения, а два других - возвра­щают конкретное значение. И это совершенно допустимо, но, как пояснялось выше, тип возвращаемого значения не играет никакой роли для перегрузки метода. Следова­тельно, попытка использовать два разных (по типу возвращаемого значения) варианта метода OvlDemo() в приведенном ниже фрагменте кода приведет к ошибке.

// Одно объявление метода OvlDemo(int) вполне допустимо.

public void OvlDemo(int a)

{

Console.WriteLine("Один параметр: " + a);

}

 

/* Ошибка! Два объявления метода OvlDemo(int) не допускаются,

хотя они и возвращают разнотипные значения. */

public int OvlDemo(int a)

{

Console.WriteLine("Один параметр: " + a);

return a * a;

}

Как следует из комментариев к приведенному выше коду, отличий в типах значе­ний, возвращаемых обоими вариантами метода OvlDemo(), оказывается недостаточно для перегрузки данного метода.

В С# предусмотрен ряд неявных (т.е. автоматических) преобразований типов. Эти преобразования распространяются также на параметры перегружаемых методов. В качестве примера рассмотрим следующую программу.

Листинг 8.18

// Неявные преобразования типов могут повлиять

// на решение перегружать метод.

 

using System;

 

class Overload2

{

public void MyMeth(int x)

{

Console.WriteLine("В методе MyMeth(int): " + x);

}

 

public void MyMeth(double x)

{

Console.WriteLine("В методе MyMeth(double): " + x);

}

}

 

class TypeConv

{

static void Main()

{

Overload2 ob = new Overload2();

 

int i = 10;

double d = 10.1;

 

byte b = 99;

short s = 10;

float f = 11.5F;

 

 

ob.MyMeth(i); // вызвать метод ob.MyMeth(int)

ob.MyMeth(d); // вызвать метод ob.MyMeth(double)

 

ob.MyMeth(b); // вызвать метод ob.MyMeth(int) –

//с преобразованием типа

ob.MyMeth(s); // вызвать метод ob.MyMeth(int) –

// с преобразованием типа

ob.MyMeth(f); // вызвать метод ob.MyMeth(double) –

// с преобразованием типа

}

}

При выполнении этой программы получается следующий результат.

В методе MyMeth(int): 10

В методе MyMeth(double): 10.1

В методе MyMeth(int): 99

В методе MyMeth(int): 10

В методе MyMeth(double): 11.5

В данном примере определены только два варианта метода MyMeth(): с параме­тром типа int и с параметром типа double. Тем не менее методу MyMeth() можно передать значение типа byte, short или float. Так, если этому методу передается зна­чение типа byte или short, то компилятор С# автоматически преобразует это зна­чение в тип int и в итоге вызывается вариант MyMeth(int) данного метода. А если ему передается значение типа float, то оно преобразуется в тип double и в результате вызывается вариант MyMeth(double) данного метода.

Следует, однако, иметь в виду, что неявные преобразования типов выполняются лишь в том случае, если отсутствует точное соответствие типов параметра и аргумента. В качестве примера ниже приведена чуть измененная версия предыдущей программы, в которую добавлен вариант метода MyMeth(), где указывается параметр типа byte.

Листинг 8.19

// Добавить метод MyMeth(byte).

 

using System;

 

class Overload2

{

public void MyMeth(byte x)

{

Console.WriteLine("В методе MyMeth(byte): " + x);

}

 

public void MyMeth(int x)

{

Console.WriteLine("В методе MyMeth(int): " + x);

}

 

public void MyMeth(double x)

{

Console.WriteLine("В методе MyMeth(double): " + x);

}

}

 

class TypeConv

{

static void Main()

{

Overload2 ob = new Overload2();

 

int i = 10;

double d = 10.1;

 

byte b = 99;

short s = 10;

float f = 11.5F;

 

 

ob.MyMeth(i); // вызвать метод ob.MyMeth(int)

ob.MyMeth(d); // вызвать метод ob.MyMeth(double)

 

ob.MyMeth(b); // вызвать ob.MyMeth(byte) –

// на этот раз без преобразования типа

 

ob.MyMeth(s); // вызвать метод ob.MyMeth(int) –

// с преобразованием типа

ob.MyMeth(f); // вызвать метод ob.MyMeth(double) –

// с преобразованием типа

}

}

Выполнение этой программы приводит к следующему результату.

В методе MyMeth(int): 10

В методе MyMeth(double): 10.1

В методе MyMeth(byte): 99

В методе MyMeth(int): 10

В методе MyMeth(double): 11.5

В этой программе присутствует вариант метода MyMeth(), принимающий аргу­мент типа byte, поэтому при вызове данного метода с аргументом типа byte выбира­ется его вариант MyMeth(byte) без автоматического преобразования в тип int.

Оба модификатора параметров, ref и out, также учитываются, когда принимается решение о перегрузке метода. В качестве примера ниже приведен фрагмент кода, в ко­тором определяются два совершенно разных метода.

public void MyMeth(int x)

{

Console.WriteLine("В методе MyMeth(int): " + x);

}

 

public void MyMeth(ref int x)

{

Console.WriteLine("В методе MyMeth(ref int): " + x);

}

Следовательно, при обращении

ob.MyMeth(i)

вызывается метод MyMeth(int x), но при обращении

ob.MyMeth(ref i)

вызывается метод MyMeth(ref int x).

Несмотря на то что модификаторы параметров ref и out учитываются, когда при­нимается решение о перегрузке метода, отличие между ними не столь существенно. Например, два следующих варианта метода MyMeth() оказываются недействите­льными.

// Неверно!

public void MyMeth(out int x) { //...

public void MyMeth(ref int x) { //...

В данном случае компилятор не в состоянии различить два варианта одного и того же метода MyMeth() только на основании того, что в одном из них используется пара­метр out, а в другом - параметр ref.

Перегрузка методов поддерживает свойство полиморфизма, поскольку именно та­ким способом в С# реализуется главный принцип полиморфизма: один интерфейс - множество методов. Для того чтобы стало понятнее, как это делается, обратимся к конкретному примеру. В языках программирования, не поддерживающих перегрузку методов, каждому методу должно быть присвоено уникальное имя. Но в программировании зачастую возникает потребность реализовать по сути один и тот же метод для обработки разных типов данных. Допустим, что требуется функция, определяю­щая абсолютное значение. В языках, не поддерживающих перегрузку методов, обычно приходится создавать три или более вариантов такой функции с несколько отличаю­щимися, но все же разными именами. Например, в С функция abs() возвращает аб­солютное значение целого числа, функция labs() - абсолютное значение длинного целого числа, а функция fabs() - абсолютное значение числа с плавающей точкой обычной (одинарной) точности.

В С перегрузка не поддерживается, и поэтому у каждой функции должно быть свое, особое имя, несмотря на то, что все упомянутые выше функции, по существу, делают одно и то же - определяют абсолютное значение. Но это принципиально усложняет положение, поскольку приходится помнить имена всех трех функций, хотя они реали­зованы по одному и тому же основному принципу. Подобные затруднения в С# не воз­никают, поскольку каждому методу, определяющему абсолютное значение, может быть присвоено одно и то же имя. И действительно, в состав библиотеки классов для среды.NET Framework входит метод Abs (), который перегружается в классе System.Math для обработки данных разных числовых типов. Компилятор С# сам определяет, какой имен­но вариант метода Abs() следует вызывать, исходя из типа передаваемого аргумента.

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

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

В С# определено понятие сигнатуры, обозначающее имя метода и список его па­раметров; Применительно к перегрузке это понятие означает, что в одном классе не должно существовать двух методов с одной и той же сигнатурой. Следует подчеркнуть, что в сигнатуру не входит тип возвращаемого значения, поскольку он не учитывается, когда компилятор С# принимает решение о перегрузке метода. В сигнатуру не входит также модификатор params.

Перегрузка конструкторов

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

Листинг 8.20

// Продемонстрировать перегрузку конструктора.

 

using System;

 

class MyClass

{

public int x;

 

public MyClass()

{

Console.WriteLine("В конструкторе MyClass().");

x = 0;

}

 

public MyClass(int i)

{

Console.WriteLine("В конструкторе MyClass(int).");

x = i;

}

 

public MyClass(double d)

{

Console.WriteLine("В конструкторе MyClass(double).");

x = (int) d;

}

 

public MyClass(int i, int j)

{

Console.WriteLine("В конструкторе MyClass(int, int).");

x = i * j;

}

}

 

class OverloadConsDemo

{

static void Main()

{

MyClass t1 = new MyClass();

MyClass t2 = new MyClass(88);

MyClass t3 = new MyClass(17.23);

MyClass t4 = new MyClass(2, 4);

 

Console.WriteLine("t1.x: " + t1.x);

Console.WriteLine("t2.x: " + t2.x);

Console.WriteLine("t3.x: " + t3.x);

Console.WriteLine("t4.x: " + t4.x);

}

}

При выполнении этой программы получается следующий результат.

В конструкторе MyClass().

В конструкторе MyClass (int).

В конструкторе MyClass(double).

В конструкторе MyClass (int, int).

tl.x: 0

t2.x: 88

t3.x: 17

t4.x: 8

В данном примере конструктор MyClass() перегружается четыре раза, всякий раз конструируя объект по-разному. Подходящий конструктор вызывается каждый раз, исходя из аргументов, указываемых при выполнении оператора new. Перегрузка кон­структора класса предоставляет пользователю этого класса дополнительные преиму­щества в конструировании объектов.

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

Листинг 8.21

// Класс для хранения символов в стеке.

 

using System;

 

class Stack

{

// Эти члены класса являются закрытыми.

char[] stck; // массив, содержащий стек

int tos; // индекс вершины стека

 

// Сконструировать пустой объект класса Stack

// по заданному размеру стека.

public Stack(int size)

{

stck = new char[size]; // распределить память для стека

tos = 0;

}

 

// Сконструировать объект класса Stack из существующего стека.

public Stack(Stack ob) {

// Распределить память для стека.

stck = new char[ob.stck.Length];

 

// Скопировать элементы в новый стек.

for(int i=0; i < ob.tos; i++)

stck[i] = ob.stck[i];

 

// Установить переменную tos для нового стека.

tos = ob.tos;

}

 

// Поместить символы в стек.

public void Push(char ch

{

if(tos==stck.Length) {

Console.WriteLine(" – Стек заполнен.");

return;

}

 

stck[tos] = ch;

tos++;

}

 

// Извлечь символ из стека.

public char Pop()

{

if(tos==0) {

Console.WriteLine(" – Стек пуст.");

return (char) 0;

}

 

tos--;

return stck[tos];

}

 

// Возвратить значение true, если заполнен.

public bool IsFull()

{

return tos==stck.Length;

}

 

// Возвратить значение true, если стек пуст.

public bool IsEmpty() {

return tos==0;

}

 

// Возвратить общую емкость стека.

public int Capacity() {

return stck.Length;

}

 

// Возвратить количество объектов, находящихся

// в настоящий момент в стеке.

public int GetNum() {

return tos;

}

}

 

// Продемонстрировать применение класса Stack.

class StackDemo

{

static void Main()

{

Stack stk1 = new Stack(10);

char ch;

int i;

 

// Поместить ряд символов в стек stk1.

Console.WriteLine("Поместить симвоы A-J в стек stk1.");

for(i=0;!stk1.IsFull(); i++)

stk1.Push((char) ('A' + i));

 

// Создать копию стека stck1.

Stack stk2 = new Stack(stk1);

 

// Вывести содержимое стека stk1.

Console.Write("Содержимое стека stk1: ");

while(!stk1.IsEmpty()) {

ch = stk1.Pop();

Console.Write(ch);

}

 

Console.WriteLine();

 

Console.Write("Содержимое стека stk2: ");

while (!stk2.IsEmpty()) {

ch = stk2.Pop();

Console.Write(ch);

}

 

Console.WriteLine("\n");

 

}

}

Результат выполнения этой программы приведен ниже.

Поместить символы A-J в стек stkl.

Содержимое стека stkl: JIHGFEDCBA

Содержимое стека stk2: JIHGFEDCBA

В классе StackDemo сначала конструируется первый стек (stkl), заполняемый символами. Затем этот стек используется, для конструирования второго стека (stk2). Это приводит к выполнению следующего конструктора класса Stack.

// Сконструировать объект класса Stack из существующего стека.

public Stack(Stack ob)

{

// Распределить память для стека.

stck = new char[ob.stck.Length];

// Скопировать элементы в новый стек.

for (int i=0; i < ob.tos; i++)

stck[i] = ob.stck[i];

// Установить переменйую tos для нового стека.

tos = ob.tos;

}

В этом конструкторе сначала распределяется достаточный объем памяти для мас­сива, чтобы хранить в нем элементы стека, передаваемого в качестве аргумента ob. За­тем содержимое массива, образующего стек ob, копируется в новый массив, после чего соответственно устанавливается переменная tos, содержащая индекс вершины стека. По завершении работы конструктора новый и исходный стеки существуют как отдель­ные, хотя и одинаковые объекты.



Поделиться:


Последнее изменение этой страницы: 2016-12-30; просмотров: 208; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 44.200.40.97 (0.314 с.)