Влияют ли геттеры и сеттеры на производительность в C++/D/Java?

Это довольно старая тема: сеттеры и геттеры хорошие или злые?

мой вопрос: Могут ли составители в C++ / D / Java текст геттеры и сеттер?

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

10 ответов


Это зависит. Нет универсального ответа, который всегда будет верным.

в Java компилятор JIT, вероятно, встроит его рано или поздно. Насколько я знаю, JVM JIT-компилятор оптимизирует только сильно используемый код, поэтому вы могли видеть накладные расходы вызова функции изначально, пока геттер/сеттер не был вызван достаточно часто.

В C++ он почти наверняка будет встроен (при условии, что оптимизация включена). Однако, есть один случай где его, вероятно, не будет:

// foo.h
class Foo {
private:
  int bar_;

public:
  int bar(); // getter
};

// foo.cpp
#include "foo.h"

int Foo::bar(){
  return bar_;
}

Если определение функции не видно пользователям класса (который будет включать foo.h, но не увидит фу.cpp), то компилятор не может быть в состоянии встроить вызов функции.

MSVC должен иметь возможность встроить его, если генерация кода времени связи включена в качестве оптимизации. Я не знаю, как GCC справляется с этой проблемой.

по расширению это также означает, что если геттер определен в другом.dll/.Итак, звоните can не подставляться.

в любом случае, я не думаю, что тривиальные get/setters обязательно "хорошая практика ООП" или что есть "все другие причины для их использования". Многие люди считают тривиальные get / setters 1) признаком плохого дизайна, а 2) пустой тратой времени.

лично, это не то, о чем я беспокоюсь в любом случае. Для меня, чтобы что-то квалифицировать как "хорошая практика ООП", оно должно иметь некоторые количественные положительные эффекты. Тривиальные get / setters имеют некоторые маргинальные преимущества, а некоторые так же незначительные недостатки. Поэтому я не думаю, что это хорошая или плохая практика. Они просто то, что вы можете сделать, если вы действительно хотите.


В D, все методы классов, а не структуры, являются виртуальными по умолчанию. Вы можете сделать метод не виртуальным, сделав метод или весь класс окончательным. Кроме того, D имеет синтаксис свойств, который позволяет сделать что-то общедоступным полем, а затем изменить его на getter/setter позже, не нарушая совместимость исходного уровня. Поэтому в D Я бы рекомендовал просто использовать общедоступные поля, если у вас нет веских причин делать иначе. Если вы хотите использовать тривиальные геттеры/сеттеры для некоторых причина, такая как только наличие геттера и создание переменной только для чтения из-за пределов класса, делает их окончательными.


Edit: например, строки:

S s;
s.foo = s.bar + 1;

будет работать как

struct S
{
     int foo;
     int bar;
}

и

struct S
{
     void foo(int) { ... }
     int bar() { ... return something; }
}

Я пробовал на Java: то же самое с геттером и сеттером и без них. Результат: нет существенной разницы между временем выполнения двух вариантах. Вот код:

class Person
{
    public int age;
    String name;
    public Person(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }

}
class GetSetPerson
{
    private int age;
    String name;
    public GetSetPerson(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }
    public void setAge(int newage)
    {
        age=newage;
    }
    public int getAge()
    {
    return age; 
    }
}
class Proba
{
//Math.hypot kb 10-szer lassabb, mint a Math.sqrt(x*xy*y)!!!

public static void main(String args[])
{
long startTime, endTime, time;
int i;int agevar;
//Person p1=new Person("Bob",21);
GetSetPerson p1=new GetSetPerson("Bob",21);
startTime=System.nanoTime();
/*
for (i=0;i<1000000000;i++)
     {
     p1.age++;

     }

*/    
for (i=0;i<1000000000;i++)
     {
     agevar=p1.getAge();
     agevar++;
     p1.setAge(agevar);
     }

endTime=System.nanoTime();
time=endTime-startTime;
System.out.println(""+time);
System.out.println(p1.name+"'s age now  is "+p1.getAge());
}

}

Я знаю, в конце Боб немного старше меня, но для этого все должно быть в порядке.


у меня был точно такой же вопрос однажды.

чтобы ответить на него, я запрограммировал две небольшие программы:

первый:

#include <iostream>

class Test
{
     int a;
};

int main()
{
    Test var;

    var.a = 4;
    std::cout << var.a << std::endl;
    return 0;
}

второй:

#include <iostream>

class Test
{
     int a;
     int getA() { return a; }
     void setA(int a_) { a=a_; }
};

int main()
{
    Test var;

    var.setA(4);
    std::cout << var.getA() << std::endl;
    return 0;
}

я скомпилировал их в ассемблер, с-O3 (полностью оптимизирован) и сравнил два файла. Они были идентичны. Без оптимизации они были разными.

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


любой JVM (или компилятор), стоящий его соли, должен поддерживать встраивание. В C++ компилятор выравнивает геттеры и сеттеры. В Java JVM вводит их во время выполнения после того, как они были вызваны "достаточно" раз. Я не знаю о D.


в зависимости от ожидаемой эволюции вашего класса get/setters могут загромождать ваш код и давать вам гибкость для расширения вашей реализации, не влияя на клиентский код.

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

учитывая скорость машины, писать лишние абстракции будет стоить вашим клиентам больше денег.

большинство компиляторов могут оптимизировать тривиальные геттеры / сеттеры, но как только вы объявляете их виртуальными (в C++), вы платите дополнительный поиск - часто стоит усилий.


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


цитирую здесь относительно языка программирования D

Я думаю, что DMD в настоящее время не может де-виртуализировать виртуальные геттеры и сеттеры. Виртуальные вызовы немного медленнее сами по себе, но они также не позволяют встроить, поэтому последовательные стандартные оптимизации не могут быть выполнены. Поэтому, если такой доступ к атрибуту является виртуальным вызовом, и это происходит в "горячей" части кода, это может значительно замедлить ваш код. (если это происходит в не-горячих частях из кода это обычно не имеет никакого эффекта. Вот почему Java Hot Spot не нужно оптимизировать весь ваш код, чтобы создать очень быструю программу в любом случае).

Я призывал Фриц ван Боммель для улучшения возможностей девиртуализации НРС:

теперь LDC может сделать это в нескольких очень простых ситуациях, но в большинстве случаев ситуация неизменна по сравнению с DMD. В конце концов LLVM улучшится, поэтому эта ситуация может улучшиться сама по себе. Но и передний конец тоже может что-то с этим сделать.

вот некоторые документы по этой теме некоторые старые, современное:


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


Я буду Эхо Xtofl.

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

Если у вас есть побочные эффекты для get или set, обязательно используйте getter или setter.

но если нет, написание геттеров и сеттеров просто загромождает код. Есть небольшой производительности. В Java, если он выполняется только несколько раз, штраф перфорации не должен иметь значения, и если он выполняется часто, он должен быть встроен, поэтому штраф смягчается. Поэтому я бы больше беспокоился о том, чтобы сделать код более трудным для чтения.

и ни на минуту не покупаю аргумент, что это устраняет проблему глобальных данных. Глобальные данные являются плохими, и их следует избегать. Но объявление поля-члена закрытым, а затем создание общедоступных геттеров и сеттеров для ничего не делает решить проблему.