Как вызвать protected метод c
Перейти к содержимому

Как вызвать protected метод c

  • автор:

Не могу в дочернем классе вызвать protected метод

Всем привет! Начал писать одну программку, и столкнулся со следующей проблемой: у меня пока 2 класса: MemObject и Vector, где Vector наследует свойства MemObject. Так вот, в классе MemObject есть чисто виртуальный метод _detectMemUse, который вычистляет память, занимаемую объектом MemObject. Фишка в том, что объект класса Vector должен уметь содержать любые объекты класса MemObject, даже ещё неизвестные. Частью этого плана является то, что при вызове метода _detectMemUse для текущего вектора нужно вызвать этот метод для всех внутренних MemObject-ов, причём в зависимости от реального типа внутреннего объекта будет вызываться свой _detectMemUse. Вот с этим вызовом и возникла продлема. Вот фрагмент кода класса Vector:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
i8 Vector::_detectMemUse(bool isUpdateMemUse) { i8 memUse=microDetectMemUse(); if (type==MEM_OBJECT) { MemObject **tmpObj=(MemObject **)data; for (i8 i=0; icellsCount; i++) { if (*tmpObj) memUse+=(**tmpObj)._detectMemUse(isUpdateMemUse); tmpObj++; } } if (isUpdateMemUse) this->memUse=memUse; return memUse; }

А вот фрагмент класса MemObject:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public: typedef void (*MemControlPointer)(i8,i8); typedef void (*MemErrorPointer)(i8,i8); protected: //Учёт памяти MemControlPointer memControl; i8 memUse; bool isBlockedMemControl; i8 currElemNum; virtual void setIsBlockedMemControl(bool val)=0; virtual i8 _detectMemUse(bool isUpdateMemUse)=0; //Ошибка выделения памяти MemErrorPointer memError;

Вот что выдаёт компилятор: «error C2248: MemObject::_detectMemUse: невозможно обратиться к protected член, объявленному в классе «MemObject» D:\doc\dev\Visual Studio 2010\VectorTest\VectorTest\Vector.cpp». Так почему я не могу обратиться к protected методу?

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Как обьявить переменную в одном классе и что бы ее было видно в дочернем классе
У меня есть основной класс с названием Root один из его членов наследует дочерний класс SubRoot.

Вызвать метод класса в другом классе
Подскажите как мне вызвать метод класса в другом классе ?

Ошибка в дочернем классе
Не могу найти ошибку в дочернем классе, возможно не правильно написать main Задание и код: Код.

Перегрузка fstream в дочернем классе
h class Child:public StudentsBD< friend fstream &operator<<( fstream& , string & ); friend.

5231 / 3203 / 362
Регистрация: 12.12.2009
Сообщений: 8,112
Записей в блоге: 2
Как наследуешь?
Делаю внезапно и красиво

Эксперт С++

1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744

class Vector : MemObject {. };

Ты ведь так унаследовал, да?
Регистрация: 01.08.2011
Сообщений: 176

1 2 3 4
#include "MemObject.h" class Vector: public MemObject {

Делаю внезапно и красиво

Эксперт С++

1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744

Попробуй не называть локальные переменные, как данные-члены класса. Чтобы вот такое не писать this->memUse=memUse.

Регистрация: 01.08.2011
Сообщений: 176

ЦитатаСообщение от Deviaphan Посмотреть сообщение

Попробуй не называть локальные переменные, как данные-члены класса. Чтобы вот такое не писать this->memUse=memUse.

А разве это поможет?

Добавлено через 2 минуты
Для интереса попробовал. Не помогло.

Делаю внезапно и красиво

Эксперт С++

1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744

По крайней мере, код станет читабельным.

Добавлено через 2 минуты
Ааааа, так ты про ошибку в строке 9. Классы Vector и MemObject это разные классы, поэтому из объекта Vector нельзя получить доступ к закрытым полям класса MemObject.

Регистрация: 01.08.2011
Сообщений: 176
Да, в строке 9

if (*tmpObj) memUse+=(**tmpObj)._detectMemUse(isUpdateMemUse);

Но ведь метод _detectMemUse класса MemObject объявлен как protected, а

class Vector: public MemObject

. То есть по-идее из класса Vector должен быть доступ к protected методам родителя. Я понимаю, если бы было private.

5231 / 3203 / 362
Регистрация: 12.12.2009
Сообщений: 8,112
Записей в блоге: 2
Тут такое дело — если б ты вызвал _detectMemUse() для this, т.е.

memUse+=this->_detectMemUse(isUpdateMemUse);

то все было бы нормально, _detectMemUse() был бы в области видимости, но поскольку ты вызываешь ее для стороннего объекта, то _detectMemUse получается private.

Добавлено через 3 минуты
пример:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
class A{ protected: int x; public: A():x(10){} }; class B : public A{ //A a; public: void f(){ std::coutx" " // std::endl; } }; int main(){ B b; b.f(); return 0; }

если раскоментировать закоментированные строки, код становиться некомпилируемым.
Регистрация: 01.08.2011
Сообщений: 176

Как-то не очень удобно сделано. И как можно получить доступ к защищённым полям класса, если невозможно обращаться к ним через this? Я думаю нужно использовать дружественые классы, но тогда нужно при создании каждого нового потомка добавлять его в список дружественных классов. А может есть другие способы?

Делаю внезапно и красиво

Эксперт С++

1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744

ЦитатаСообщение от Виктор_Сен Посмотреть сообщение

И как можно получить доступ к защищённым полям класса, если невозможно обращаться к ним через this?
Используя методы доступа. Или friend.
Регистрация: 01.08.2011
Сообщений: 176

А если, например, создать в классе MemObject метод callDetectMemUse, который будет получать указатель на объект MemObject и возвращать результат _detectMemUse, но уже вызванный для этого объекта, и вызывать callDetectMemUse через this. Тогда произойдёт «плавный» вызов нужного метода от имени класса MemObject. Вот что у меня получилось (_detectMemUse я переименовал в insideDetectMemUse, чтоб не путаться, поскольку detectMemUse у меня в этом классе тоже есть): MemObject.h:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#pragma once class MemObject { public: //Указатель на функцию контроля памяти //(вызывается при памяти) //Параметры: размер блока памяти и номер объекта, // память //Функция позволяет вызывать механизм // данных при // памяти typedef void (*MemControlPointer)(i8,i8); //Указатель на функцию, //вызываемую при перехвате исключений, //генерируемых при выделении памяти //Параметры: размер блока памяти, //при выделении которого призошла ошибка //и номер объекта-источника исключения typedef void (*MemErrorPointer)(i8,i8); protected: //Указатель на функцию контроля памяти и //флаг блокировки этого контроля MemControlPointer memControl; bool isBlockedMemControl; //Используемая обектом память i8 memUse; //Номер текущего объекта для функции // памяти i8 currElemNum; //Функция изменения флага блокировки контроля памяти //Вызывается рекурсивно для всех внутренних объектов virtual void setIsBlockedMemControl(bool val)=0; //Подсчитывает занимаемую объектом память, и, //в зависимости от флага isUpdateMemUse, //обновляет значение поля memUse virtual i8 insideDetectMemUse(bool isUpdateMemUse)=0; //Получает указатель на MemObject и //вызывает insideDetectMemUse от его имени i8 callInsideDetectMemUse(bool isUpdateMemUse, MemObject *memObj); //Получает указатель на MemObject и //вызывает setIsBlockedMemControl от его имени void callSetIsBlockedMemControl(bool val, MemObject *memObj); //Указатель на функцию, вызываемую при //ошибке выделения памяти MemErrorPointer memError; public: //Деструктор //Вызывает memControl(-memUse,currElemNum), //если флаг isBlockedMemControl==false //Поэтому при удалении дочернего объекта //не нужно заботиться об учёте факта //освобождения памяти virtual ~MemObject(void); //Конструктор MemObject( MemControlPointer memControl, bool isBlockedMemControl, i8 currElemNum, MemErrorPointer memError); //Обёртка insideDetectMemUse, //которая вызывает insideDetectMemUse //с параметром false //Это нужно, чтобы пользователь класса //не мог изменять поле memUse, //например, если поле memUse хранит //не всю память, а только память, //занимаемую текущим объектом без учёта //внутренних объектов i8 detectMemUse(); //Доступ к полям MemControlPointer getMemControl(); bool getIsBlockedMemControl(); i8 getMemUse(); i8 getCurrElemNum(); MemErrorPointer getMemError(); };

Фрагмент кода Vector.cpp:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
//Вычисление занимаемой объектом памяти //Параметр - флаг обновления текущего значения используемой памяти //Возвращаемое значение - занимаемая память i8 Vector::insideDetectMemUse(bool isUpdateMemUse) { //Подсчёт памяти без учёта внутренних объектов i8 currMemUse=microDetectMemUse(); //Если есть внутренние объекты if (type==MEM_OBJECT) { MemObject **tmpObj=(MemObject **)data; for (i8 i=0; icellsCount; i++) { //Если ссылка на внутренний объект не нулевая, то вызов //вычислителя памяти для внутреннего объекта if (*tmpObj) currMemUse+= callInsideDetectMemUse(isUpdateMemUse,*tmpObj); tmpObj++; } } //Обновление поля занимаемой памяти if (isUpdateMemUse) memUse=currMemUse; return currMemUse; }

Добавлено через 7 минут
В сторке 50 MemObject.h искомая функция-обёртка для insideDetectMemUse, а в сторке 18 Vector.cpp её вызов.

Модификаторы private и protected, переопределение методов, полиморфизм

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

class Properties { private int id; int width, color; Properties() { System.out.println("Конструктор Properties"); } } class Line extends Properties { double x1, y1; double x2, y2; Line() { System.out.println("Конструктор Line"); } }

Теперь, обращаясь к полю id из дочернего класса:

class Line extends Properties {Line() { System.out.println("Конструктор Line, + id); } }

возникнет ошибка. То же самое и с методами. Если в базовом классе определить какой-либо метод, например, сеттер:

class Properties {void setProp(int width, int color) { this.width = width; this.color = color; }}

то он автоматически унаследуется дочерним. Но, если указать модификатор private:

private void setProp(int width, int color) {}

то он будет недоступен в дочернем классе Line. Все остальные режимы доступа разрешают наследование элементов класса. Вот так можно управлять механизмом наследования с помощью модификатора доступа private.

Режим доступа protected

Но здесь возникает вопрос: а как задавать поля или методы, которые бы могли наследоваться, но к которым нельзя было бы обратиться извне? Как мы видели, режим private для этого не подходит – он запрещает наследование элементов. Здесь нам на помощь приходит другой режим доступа под названием protected. Он разрешает наследование элементов, но закрывает внешний доступ к полям и методам, если класс определен в другом пакете. В текущем пакете внешний доступ к полю или методу protected будет разрешен. Например, если пометить поле id как protected, то оно унаследуется дочерним классом Line:

class Properties { protected int id; . } class Line extends Properties { double x1, y1; double x2, y2; Line() { id = 1; System.out.println("Конструктор Line с style="color: #339933;">+id); } }

Теперь, при запуске программы мы увидим значение При этом, извне мы также можем обратиться к этому полю, так как находимся в текущем пакете:

line.id = 5; // ошибки нет

Но, если переместить определение класса Line в другой пакет, то прямого доступа к id уже не будет и строчка:

line.id = 5; // будет ошибка

выдаст ошибку. Вот так (в отличие от некоторых других языков программирования) работает модификатор protected в Java.

Переопределение методов и динамическая диспетчеризация

В языке Java мы можем не только наследовать методы базового класса и использовать их в дочерних, но и переопределять их. Например, объявим в базовом классе Properties метод draw() для рисования графического примитива. Очевидно, что базовый класс «не знает» о конкретном примитиве, поэтому этот метод будет иметь некую общую реализацию:

class Properties { . void draw() { System.out.println("Этот метод следует переопределить"); } . }

Если теперь его вызвать из объекта класса Line:

line.draw();

то увидим ожидаемое сообщение. Но, переопределив этот метод в дочернем классе Line:

@Override void draw() { System.out.println("Рисование линии"); }

Получаем уже вызов этого переопределенного метода. (Здесь нотация @Override – это необязательное указание переопределения метода. Ее можно не записывать, но правило хорошего тона программирования предполагает ее использование).

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

Properties p = line; p.draw();

то вызовется не метод базового класса, а переопределенный метод дочернего класса. Такой эффект возникает благодаря работе механизма под названием «динамическая диспетчеризация методов». Логика его работы проста: если он видит, что метод базового класса имеет переопределение в дочернем, то вызов переходит именно к последнему, переопределенному методу:

Здесь в обоих случаях будет вызван метод дочернего класса Line, т.к. он имеет последнее переопределение метода draw() базовых классов.

Полиморфизм

В частности, благодаря динамической диспетчеризации реализуется полиморфизм классов в Java. Здесь единый интерфейс draw() может иметь множество реализаций через переопределения в дочерних классах. В результате, можно выполнять рисование графических примитивов через ссылки Properties на базовый класс:

final int N = 2; Properties p[] = new Properties[N]; p[0] = new Line(); p[1] = new Triangle(); for(int i = 0;i  N;++i) p[i].draw();

Видите, как это удобно. Мы определили один метод draw() и, затем, через него работаем со множеством графических примитивов. Мало того, если в будущем потребуется добавить еще классы примитивов, например, дугу или круг, то менять основную логику программы не придется. Все изменения на себя возьмет динамическая диспетчеризация. В этом сила третьего кита ООП по имени полиморфизм.

Запрет наследования через final

В ряде случаев нам может потребоваться запретить наследование классов и переопределение методов. Например, определим сеттер setProp() в базовом классе. И мы бы не хотели, чтобы программист намеренно или случайно мог сделать переопределение этого метода. Модификатор private в этом случае нам не подойдет, т.к. предполагается доступ к сеттеру извне класса. Как же поступить? Здесь на помощь нам приходит ключевое слово final, которое не ограничивает режим доступа, но запрещает дальнейшее изменение метода. То есть, если определить сеттер вот так:

class Properties { . final void setProp(int width, int color) { this.width = width; this.color = color; } . }

то переопределить его в дочерних классах уже будет невозможно и запись:

class Line extends Properties { . void setProp(int width, int color) { } . }

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

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

class Properties { final int id = 5; . } class Line extends Properties { int id = 5; . }

Все работает. Разумеется, переменные id в обоих классах – это совершенно разные переменные.

Вот так с помощью ключевого слова final можно управлять механизмом наследования.

Путь кодера

Подвиг 1. Запишите базовый класс Graph для представления графиков с полями: массив из N значений (значения графика), название. И производные от него классы: LineGraph (для линейного графика), Chart (для круговой диаграммы), Bar (для столбикового графика). В дочерних классах следует реализовать перегрузку метода draw() базового класса Graph для рисования графика в соответствующем виде (рисование – это вывод в консоль сообщения, что рисуется такой-то график с такими-то значениями). Создать несколько экземпляров дочерних классов со ссылками на них типа Graph. Через эти ссылки вызвать метод draw() и убедиться в работоспособности механизма динамической диспетчеризации (вызовов методов из дочерних классов).

  • добавление/удаление книги из библиотеки;
  • вывод информации по всем книгам;
  • поиск книг по автору, а также по году издания.

Как использовать protected метод в чужого класса?

Добрый день! Не могу разобраться, как в своём наследующем классе использовать protected метод чужого базового класса.
Например, есть у меня класс MyClass , я наследую класс YourClass с методом
protected void meth1() Я ничего не могу менять в чужом классе, геттеры и сеттеры не помогут, тогда как мне использовать этот метод, какие способы возможны? Спасибо.

Отслеживать
60 10 10 бронзовых знаков
задан 7 авг 2013 в 12:07
21 4 4 бронзовых знака
Опишит подробно кто кого наследует. И из какого места требуется получить доступ к protected методу
7 авг 2013 в 12:36
7 авг 2013 в 14:57

Приведите код. Непонятно, где проблема: protected-методы из объектов порождённых классов всегда можно было использовать без проблем.

7 авг 2013 в 18:28

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

В данном случае есть самый простой вариант — создать в своем классе public метод, который и будет осуществлять вызов protected метод у предка.

Отслеживать
ответ дан 7 авг 2013 в 12:21
Dmitry Lepeshkin Dmitry Lepeshkin
756 4 4 серебряных знака 8 8 бронзовых знаков

Вы можете без проблем использовать методы, помеченные модификатором доступа protected , в классах-наследниках. Для этого и был введен данный модификатор доступа, этим он и отличается от private

Ключевое слово protected является модификатором доступа к члену. Доступ к члену с модификатором protected возможен внутри класса и из производных экземпляров класса.

public class YourClass < protected int YourMethod() < return 1; >> public class MyClass : YourClass < public MyClass () < var a = YourMethod(); // вернет 1 >> 

Подробнее почитать про protected тут

Урок №157. Наследование и cпецификатор доступа protected

На предыдущих уроках мы говорили о том, как работает наследование в языке C++. Во всех наших примерах мы использовали открытое наследование.

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

Оглавление:

  1. Спецификатор доступа protected
  2. Когда следует использовать спецификатор доступа protected?
  3. Типы наследований. Доступ к членам
    • Наследование типа public
    • Наследование типа private
    • Наследование типа protected
  4. Финальный пример
  5. Заключение

Спецификатор доступа protected

Мы уже рассматривали спецификаторы доступа private и public, которые определяют, кто может иметь доступ к членам класса. В качестве напоминания: доступ к public-членам открыт для всех, к private-членам доступ имеют только члены того же класса, в котором находится private-член. Это означает, что дочерние классы не могут напрямую обращаться к private-членам родительского класса!

class Parent

int m_private ; // доступ к этому члену есть только у других членов класса Parent и у дружественных классов/функций (но не у дочерних классов)

int m_public ; // доступ к этому члену открыт для всех объектов

Примечание: public = «открытый», private = «закрытый», protected = «защищенный».

В языке C++ есть третий спецификатор доступа, о котором мы еще не говорили, так как он полезен только в контексте наследования. Спецификатор доступа protected открывает доступ к членам класса дружественным и дочерним классам. Доступ к protected-члену вне тела класса закрыт.

class Parent
int m_public ; // доступ к этому члену открыт для всех объектов

int m_private ; // доступ к этому члену открыт только для других членов класса Parent и для дружественных классов/функций (но не для дочерних классов)

int m_protected ; // доступ к этому члену открыт для других членов класса Parent, дружественных классов/функций, дочерних классов

class Child : public Parent
m_public = 1 ; // разрешено: доступ к открытым членам родительского класса из дочернего класса
m_private = 2 ; // запрещено: доступ к закрытым членам родительского класса из дочернего класса
m_protected = 3 ; // разрешено: доступ к защищенным членам родительского класса из дочернего класса
Parent parent ;
parent . m_public = 1 ; // разрешено: доступ к открытым членам класса извне
parent . m_private = 2 ; // запрещено: доступ к закрытым членам класса извне
parent . m_protected = 3 ; // запрещено: доступ к защищенным членам класса извне

В примере, приведенном выше, вы можете видеть, что член m_protected класса Parent напрямую доступен дочернему классу Child, но доступ к нему для членов извне — закрыт.

Когда следует использовать спецификатор доступа protected?

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

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

Типы наследований. Доступ к членам

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

public;

private;

protected.

Для определения типа наследования нужно просто указать нужное ключевое слово возле наследуемого класса:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *