Как не являющиеся членом функции улучшают инкапсуляцию

Сценарий для выполнения этого замените 'DB_NAME' базой данных для уничтожения всех соединений с:

USE master
GO

SET NOCOUNT ON
DECLARE @DBName varchar(50)
DECLARE @spidstr varchar(8000)
DECLARE @ConnKilled smallint
SET @ConnKilled=0
SET @spidstr = ''

Set @DBName = 'DB_NAME'
IF db_id(@DBName) < 4
BEGIN
PRINT 'Connections to system databases cannot be killed'
RETURN
END
SELECT @spidstr=coalesce(@spidstr,',' )+'kill '+convert(varchar, spid)+ '; '
FROM master..sysprocesses WHERE dbid=db_id(@DBName)

IF LEN(@spidstr) > 0
BEGIN
EXEC(@spidstr)
SELECT @ConnKilled = COUNT(1)
FROM master..sysprocesses WHERE dbid=db_id(@DBName)
END
17
задан Luigi Ballabio 5 June 2018 в 12:32
поделиться

8 ответов

Вопрос 1

В этом случае, следуя алгоритму Мейерса, вы получите функции-члены:

  • Должны ли они быть виртуальными? Нет.
  • Это оператор << или оператор >> ? Нет.
  • Нужны ли им преобразования типов? Нет.
  • Могут ли они быть реализованы в рамках публичного интерфейса? Нет.
  • Так что сделайте их членами.

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

Вопрос 2

Функции SetXXXX должны иметь доступ к внутреннему (частному) представлению класса, поэтому они не могут быть -член не друзей; поэтому, утверждает Мейерс, они должны быть членами, а не друзьями.

Инкапсуляция происходит за счет сокрытия деталей того, как реализован класс; вы определяете публичный интерфейс отдельно от частной реализации. Если вы затем изобрели лучшую реализацию, вы можете изменить ее, не меняя общедоступный интерфейс, и любой код, использующий класс, будет продолжать работать. Таким образом, «количество функций, которые могут быть нарушены» Мейерса учитывает функции-члены и дружественные функции (которые мы можем легко отследить, взглянув на определение класса), но не любые не являющиеся членами и не дружественные функции, использующие класс через его открытый интерфейс.

Вопрос 3

На этот был дан ответ .

Важные моменты, которые следует извлечь из совета Мейерса:

  • Разрабатывайте классы так, чтобы они имели чистые, стабильные общедоступные интерфейсы, отделенные от их частной реализации ;
  • Делайте функции членами или друзьями только тогда, когда им действительно нужен доступ к реализации;
  • Подружитесь с функциями только тогда, когда они не могут быть участниками.
17
ответ дан 30 November 2019 в 12:36
поделиться

Вопрос: 2

Скотт Мейерс также предложил следующее, если вы помните:

-> Сохранить интерфейс класса завершен и минимален.

См. следующий сценарий:

class Person {

private: string name;

         unsigned int age;

         long salary;

public:

       void setName(string);// assme the implementation

       void setAge(unsigned int); // assme the implementation

       void setSalary(long sal); // assme the implementation

       void setPersonData()
       {
           setName("Scott");
           setAge(25);
           selSalary(50000);
        }
}

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

   void setPersonData(Person &p) 
   {           
       p.setName("Scott");
       p.setAge(25);
       p.selSalary(50000);
    }
1
ответ дан 30 November 2019 в 12:36
поделиться

Пока мы не сохраним сигнатуру метода неповрежденный, когда реализация класса изменения, ни один клиентский код не сломается и он хорошо инкапсулирован, правда? То же самое касается не членов функции. Так что же преимущество функции, не являющейся членом предоставляет?

Мейерс говорит, что класс с большим количеством методов менее инкапсулирован, чем класс с меньшим количеством методов, потому что реализации всех этих внутренних методов могут быть изменены. Если бы какой-либо из методов не являлся членами, это уменьшило бы количество методов, на которые могут повлиять внутренние изменения класса.

То, что он имел в виду под f, требует типа преобразование крайнего левого аргумента?

Я думаю, он имеет в виду операторы, функции, которые имели бы неявный левый аргумент, если бы они были функциями-членами.

0
ответ дан 30 November 2019 в 12:36
поделиться

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

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

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


Еще одна вещь, вероятно, - это интерфейс, полностью состоящий из геттеров и сеттеров. Это вряд ли что-то инкапсулирует.

В частности, в случае Point у вас может возникнуть соблазн хранить данные как int coords [2] , и в этом отношении геттеры и сеттеры могут иметь значение (но можно также всегда учитывать простоту использования и простоту реализации).

Но если вы перейдете к более сложным классам, они должны сделать что-то (некоторые основные функции), кроме простого предоставления доступа к своим данным.


Когда дело доходит до вектора, некоторые из его методов могли быть бесплатные функции: назначить (в терминах clear + insert), at, back, фронт (с точки зрения размера + оператор [] ), пустой (с точки зрения размера или начало / конец), pop_back (стирание + размер), push_back (вставка + размер), конец (начало + размер), rbegin и rend (начало и конец).

Но если рассматривать это строго, это может привести к довольно запутанным интерфейсам, например

 for (vector<T>::iterator it = v.begin(); it != end(v); ++it)

Кроме того, здесь следует учитывать возможности других контейнеров. Если std :: list не может реализовать end как бесплатную функцию, то std :: vector тоже не должен (шаблонам нужен один единый шаблон для итерации по контейнеру).

Опять же, используйте здравый смысл.

это может привести к довольно запутанным интерфейсам, например

 for (vector<T>::iterator it = v.begin(); it != end(v); ++it)

Кроме того, здесь нужно будет рассмотреть возможности других контейнеров. Если std :: list не может реализовать end как бесплатную функцию, то std :: vector тоже не должен (шаблонам нужен один единый шаблон для итерации по контейнеру).

Опять же, используйте здравый смысл.

это может привести к довольно запутанным интерфейсам, например

 for (vector<T>::iterator it = v.begin(); it != end(v); ++it)

Кроме того, здесь нужно будет рассмотреть возможности других контейнеров. Если std :: list не может реализовать end как бесплатную функцию, то std :: vector тоже не должен (шаблонам нужен один единый шаблон для итерации по контейнеру).

Опять же, используйте здравый смысл.

1
ответ дан 30 November 2019 в 12:36
поделиться

Значение f требует преобразования типов в крайнем левом аргументе следующим образом:

рассмотрите следующий вариант:

Class Integer 
{  
    private: 
       int num;
     public:
        int getNum( return num;)
        Integer(int n = 0) { num = n;}

    Integer(const Integer &rhs)) { num = rhs.num ;}
    Integer operator * (const Integer &rhs)
    {
         return Integer(num * rhs.num);
    }
}


int main()
{
    Integer i1(5);

    Integer i3 = i1 *  3; // valid 

    Integer i3 = 3 * i1 ; // error     
}

В приведенном выше коде i3 = i1 * 3 эквивалентен this-> operator * (3) , который действителен, поскольку 3 неявно преобразуется в целое число.

Где, как и в более позднем i3 = 3 * i1 , эквивалентно 3.operator * (i1) , согласно правилу, когда оператор перегрузки u с использованием функции-члена, вызывающий объект должен быть того же класса. но здесь дело не в этом.

Чтобы заставить Integer i3 = 3 * i1 работать, можно определить функцию, не являющуюся членом, следующим образом:

Integer operator * (const Integer &lhs , const Integer &rhs) // non-member function
    {

         return Integer(lhs.getNum() * rhs.getNum());

    }

Я думаю, вы получите представление из этого примера .... .

9
ответ дан 30 November 2019 в 12:36
поделиться

Из четырех случаев, которые он предоставляет для создания функций, не являющихся членами, ближайший предложенные вами методы vector сводятся к следующему:

else if (f can be implemented via C's
         public interface)
   make f a non-member function;

Но вы не можете реализовать такие методы, как push_back , insert или operator [] через общедоступный интерфейс. Те являются общедоступным интерфейсом. Возможно, можно реализовать push_back в терминах insert , но в значительной степени какой публичный интерфейс вы собираетесь использовать для таких методов?

Далее примеры для дружбы с функциями, не являющимися членами, на мой взгляд, особый случай, operator << Оператор и >> , а также преобразование типов потребуют очень точных и неотфильтрованных данных из класса. Эти методы, естественно, очень инвазивны.

Хотя я не поклонник доктора Доббса или кого-либо из заявленных «гуру C ++», я думаю, что в этом случае вы можете дважды угадать свою собственную реализацию. Алгоритм Скотта Мейера мне кажется разумным.

1
ответ дан 30 November 2019 в 12:36
поделиться

Он конкретно говорит, что "не-членские не дружественные функции "(курсив мой). Если вам нужно сделать функцию, не являющуюся членом, злодеем, его алгоритмы говорят, что она должна быть функцией-членом, если только она не operator >> или operator << или не требует преобразования типов в ее крайнем левом аргументе.

0
ответ дан 30 November 2019 в 12:36
поделиться

Внимательно изучите алгоритмы STL. sort , copy , transform и т. Д. Работают с итераторами и не являются функциями-членами.

Вы также ошибаетесь насчет его алгоритма. Функции set и get не могут быть реализованы с помощью открытого интерфейса Point.

1
ответ дан 30 November 2019 в 12:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: