Вы находитесь на странице: 1из 4

3.2.

- b) Если есть ошибки в реализации методов заданных классов и функции main (),
исправьте их, используя операцию разрешения области видимости «::».
Какие конструкторы и деструкторы и в каком порядке будут вызываться при работе данной программы?
Отлично, есть кое где замечания -Фалько Ник.Серг. -желтым цветом
#include <iostream>
using namespace std;

int x = 0; // глобальная переменная x


int f() { return x = 1; } // глобальная функция f() , которая возвращает значение x = 1

class A { // объявление класса A


int x; // поле x
public:
A( int n = 2) { x = n; } // конструктор по умолчанию и с параметром , который инициализирует
поле x , значением параметра - n
int f() { return x = 3; } // метод f() - принадлежащий классу A - возвращает значение x -
равное 3
int f(int a, int b) { return x = a % b; } // перегруженный метод f(int, int) , принимающий два
целочисленных параметра , и возвращающий значение поле x - равное остатку от деления , переменных a и b
};

class B: public A { // Создаем дочерний класс B , и наследуем его от родительского класса A


int x; // поле x
public:
int f(int a) { return x = a; } // метод f(int a) - принадлежащий классу B , который
инициализирует поле x -значением переменной "a" - и возвращает её значение
int f(int a, int b) { return x = a + b; } // метод родительского класса A - int f(int a, int
b) , который мы переопределили в наследнике B – это НЕВЕРНО – virtual нет – здесь override, то есть
задали ему другое поведение , и теперь он инициализирует поле x - суммой a и b - а далее возвращает
значение суммы
int g(A * a, B * b); // сигнатура метод g () , который принимает указатели на объекты класса A и
класса B.
};

int B::g (A * pa, B * pb) {


x = A::f (); // 1) Ошибка - в этой строке , компилятор попытается вызвать функции B::f(int) ,
и B::f(int , int ) . Но не сможет этого сделать из-за несоответствия передаваемых и ожидаемых
параметров , поэтому чтобы решить эту проблему , нужно явно указать область видимости - A::f () ,
или ::f() . Ну или же , сделать так , чтобы количество передаваемых параметров , соответствовало
функции B::f(int) , или же B::f(int , int)
x = f (5); // тут все хорошо , в этой строке вызывается метод класса B::f(int a) . Область
видимости B:: - можем явно не указывать
x = f (6, 6); // тут вызывается переопределенный метод B::f(int a, int b) . Так же может быть
вызвана функция A::f(int a, int b) , если мы явно укажем области видимости - A::
x = B::f(5); //2) Ошибка - в классе A - нет функции f(int). Поэтому надо убрать A:: , или же по
желанию уточнить область видимости - B:: . Так же можем указать глобальную область видимости , но в
таком случаем придется убрать передаваемый параметр 5.
return -1; // возвращаем -1
}

int main () {
B a; // создаем экземпляр "a" класса B Конструкторы : A() , B() важен порядок
class C { // Объявляем класс C , который содержит объект "b" типа B , и объект "a" , типа A
B b;
A a;
public:// порядок иниц Прата стр 737 попробуйте записать в обратном порядке C{ A a;B b; public :…..
C(): b(), Конструкторы : A() , B() важен порядок a (b) A ( const A & ) важно ктор копирования
для А но передается УЖЕ созданный В b . И только в конце C() { } // для объекта b - будет вызван ctor
по умолчанию. А для объекта a - будет вызван ctor , копирования
};
C c; // создаем экземпляр "с" класса С
x = a.A::f (); // 3) Ошибка - Тут нужно явно указать область видимости A::f() . Так как
компилятор для объекта "a", типа "B" - будет пытаться найти функцию B::f() , но у него не получиться
это сделать , поэтому он будет пытаться взывать функции B::f(int) и B::f(int , int) , но не сможет из-
за не хватки передаваемых параметров
x = a.f (7); // Тут мы можем явно не указывать область видимости функции , так как в любом
случае будет вызван метод B::f(int)
return a.g (& a, & a); // И тут тоже необязательно явно указывать область видимости B:: . Кроме того
формальный параметр "A *pa" - может принимать ссылку на объект "a" , типа B . Так как класс A -
родитель класса B
}

// Конструкторы : A() , B() , A() , B() , A ( const A & ) , C()


// Деструктор : ~С() ,~A(), ~B() , ~A() , ~B() , ~A() Дторы автоматически инверсия ряда кторов

4.2. Есть ли ошибки в приведенном фрагменте программы? Если есть, то объясните, в чем они
заключаются.
Ошибочные конструкции вычеркнуть из текста программы. Что будет выдано в стандартный канал вывода
при работе программы?

#include <iostream>
using namespace std;

class X { // объявление класса X

public:
virtual int g (double x) { // виртуальная функция g - принимающая формальный параметр double x
h (); // вызов функции X::h()
cout << "X::g" << endl; // вывод на экран
return 1; // возвращаем значение 1
}

void h () { t (); cout << "X::h" << endl;} // функция h , которая вызывает виртуальную функцию t()
virtual void t () { cout << "X::t" << endl;} // виртуальная функция t ()
};

class Z: public X { // класс Z , который наследуется от X


public:
int g (double y) { // переопределяем поведение функции g()
h (); // вызов виртуальной НЕВЕРНО функции Z::h()
cout << "Z::g" << endl; // вывод на экран
return 3; // возвращаем значением 3
}
virtual void h () { t (1); cout << "Z::h" << endl; } // виртуальная СТРОГО говоря так можно говорить
если есть наследник от Z функция h , которая вызывает другую виртуальную функцию Z::t(int)
virtual void t (int k) { cout << "Z::t" << endl; } // виртуальная функция t() НЕВЕРНО!!!
};

int main() {
X a; // создаём объект "a" класса X
Z b; // создаём объект "b" класса Z
X * p = &b; // указатель базового класса X - ссылается на объект b , класса Z ( наследника )
p -> g(1.5); // косвенный вызов функции g - будет вызвана функция , Z::g( double ) ( динамическое
связывание )
p -> h(); // Вызов функции X::h() ( Почему не Z::h() ? )Потому что НЕТ virtual в БАЗЕ и h не вирт
// Здесь override
//p -> t(5); // Ошибка - Функция X::t() - не может быть вызвана , так как мы передаем параметр 5 .
Компилятор попытается вызвать виртуальную ОШИБКА функцию X::t()
// но не сможет этого сделать , так как количество передаваемых параметров , больше чем количество
ожидаемых
}

Вывод на экран :
// Z::t
// Z::h
// Z::g
// X::t
// X::h
3.2. - с) Если есть ошибки в реализации методов заданных классов и функции main (),
исправьте их, используя операцию разрешения области видимости «::».
Какие конструкторы и деструкторы и в каком порядке будут вызываться при работе данной программы?

#include <iostream>
using namespace std;

int x = 0; // глобальная переменная x


int f (int a, int b) { return x = a + b; } // глобальная функция ::f(int a, int b) - которая
возвращает сумму принимаемых параметров
class A {
int x; // поле x
public:
A(int n = 1) { x = n; } // конструктор по умолчанию и с параметром
int f() { return ::x = x; } // метод A::f() , который инициализирует глобальную переменную ::x -
значением поля A::x
};

class B { // объявление класса B


int x;
public:
B (int n = 2) { x = n; } // конструктор по умолчанию , и с параметром
};

class C: public A, public B { // класс C , который наследуется от классов A и B


int x;
public:
int f(int a) { return ::x = x; } // метод C::f(int) , который инициализирует глобальную
переменую ::x - значением поля x
void g (); // прототип функции g()
};

void C::g() {
x = A::f (); // 1) Ошибка - тут надо явно указать область видимости A::f() . Так как
компилятор попытается взывать функцию C::f(int) , но не сможет этого сделать из-за отсутствия
передаваемого параметра
f (3); // тут вызовется метод C::(int)
x = ::f (4, 5); // 2) Ошибка - в этой строке тоже необходимо указать области видимости " ::f "
, чтобы вызвалась функция ::f(int a , int b ) . Иначе компилятор попытается вызвать функцию С::f(int) -
но у него не получиться это сделать из-за того , что функция ожидает 1 параметр , а мы передаем ей - 2
x = 6; // поле x - инициализируется значением 6
}

int main () {
C c; // создаем объект "c" , класса C
B b; // создаем объект "b" , класса B
A a; // создаем объект "a" , класса A
c.A::f(); // 3) Ошибка - чтобы вызвался метод A::f() - нужно явно указать его область видимости , то
есть - A:: . Иначе компилятор снова попытается вызвать C::f(int)
c.f(7); // вызываем метод C::f()
x = f('8', 9); // Вызывается глобальная функция f::(int , int ) . А фактический параметр (char) -
'8' , приводиться к int , с помощью таблицы ASCII . Таким образом (char) '8' = (int) 56
return -1;
}

//Порядок вызова конструкторов - A() , B() , C() , B() , A() Важен порядок class C: public A,
public B
//Порядок вызова деструкторов - ~A() , ~B() , ~C() , ~B() , ~A() Автоматом обратно кторам
3.4. Если есть ошибки в следующем фрагменте, то в чем они заключаются ?
//Исправьте ошибки, ничего не удаляя, добавив в общей сложности не более 12 символов.
#include <iostream>
using namespace std;

class S { // объявляем класс S


public:
int s; // public поле s
void sp(int si) { s = si; } // метод sp , который инициализирует поле s - значением формального
параметра si
};

class T: public S { // объявление класса T , который " приватно " наследуется от класса S
public:
int t; // public поле t
void tp(int ti) { t = ti; s = ti; } // функция tp , которая инициализирует поля "t" и "s" -
значениями параметра ti.
// Не смотря на то , что класс S - наследуется приватно . Мы можем получить доступ к переменной
S::s - так как при private наследовании :
// Protected и public члены базового класса S - становятся private в дочернем классе T.
};

class U: public T { // объявление класса U , который " приватно " наследуется от класса T
public:
int u; // поле u
void up(int ui) { u = ui; t = ui; s = ui; } // функция up , которая инициализирует поля "u" ,
"t" и "s" - значениями параметра ui .
// 1) Тут возникнет ошибочка , так как благодаря private наследованию - переменная S::s -
изменила свой модификатор доступа на private в классе T , который " приватно " наследуется от класса S
};

void g() { // глобальная функция ::g()


U * pu = new U; // Создаем указатель "pu" типа "U" , и с помощью оператора new , возвращаем
адрес объекта "U" , в памяти
T * pt = pu; // Создаем указатель "pt" типа "T" , и пытаемся присвоить ему , адрес указателя
"pu" , типа "U"
// 2) Однако , на этой строке возникает ошибка - так как тут не
будет работать динамическое связывание , потому что класс U " приватно " наследуется от класса T .
// И соответственно указатель типа T (база) - не может ссылаться на
указатель типа U (потомок) .
S * ps = pu; // Создаем указатель "ps" типа "S" , и пытаемся присвоить ему , адрес указателя
"pu".
// 3) Тут такая же ошибка : Из-за private наследования , базовый
класс не может ссылаться на своих потомков
// Чтобы исправить все ошибки - достаточно просто во всех местах , изменить спецификатор наследования
на public.
// В сумме как раз получается 12 символов - 2 * strlen("public") . По этому примеру отлично, можно (но
//не обязательно!!) добавить .Возможен еще один вариант
// 1) class T: public S добавить 6 символов
// 2) Преобразовать указатели
// T * pt = (T*)pu;//(T *)
// S * ps = (S*)pu;//(S *) В итоге 6 + 4 +4= 14 символов
}

Вам также может понравиться