Практика кодирования в C ++, что вы выбираете и почему? - PullRequest
1 голос
/ 11 ноября 2009

У меня есть большой объект, скажем, MyApplicationContext, который хранит информацию о MyApplication, такую ​​как имя, путь, loginInformation, описание, детали и другие.

// MyApplicationCtx

class MyApplicationCtx{
       // ....
    private:
        std::string name;
        std::string path;
        std::string desciption;
        struct loginInformation loginInfo;
        int appVersion;
        std::string appPresident;
        //others
}

это мой метод cloneApplication (), который фактически устанавливает новое приложение. Есть два способа сделать это, как показано в коде 1 и коде 2. Какой из них я предпочитаю и почему?

// Код 1

public void cloneApplication(MyApplicationCtx appObj){
    setAppName(appObj);
    setAppPath(appObj);
    setAppAddress(&appObj); // Note this address is passed
    setAppDescription(appObj);
    setAppLoginInformation(appObj);
    setAppVersion(appObj);
    setAppPresident(appObj);
}

public void setAppLoginInformation(MyApplicationCtx appObj){
    this->loginInfo = appObj.loginInfo; //assume it is correct
}

public void setAppAddress(MyApplicationCtx *appObj){
    this->address = appObj->address;
}

 .... // same way other setAppXXX(appObj) methods are called.

Q1. Влияет ли передача большого объекта appObj каждый раз на производительность?

Q2. Если я передам его по ссылке, как это повлияет на производительность?

public void setAppLoginInformation(MyApplicationCtx &appObj){ 
    this->loginInfo = appObj.loginInfo;
}

// Код 2

public void setUpApplication(MyApplicationCtx appObj){
    std::string appName;
    appName += appOj.getName();
    appName += "myname";
    setAppName(appName);

    std::string appPath;
    appPath += appObj.getPath();
    appPath += "myname";
    setAppPath(appPath);

    std::string appaddress;
    appaddress += appObj.getAppAddress();
    appaddress += "myname";
    setAppAddress(appaddress); 

    ... same way setup the string for description and pass it to function
    setAppDescription(appdescription);

    struct loginInformation loginInfo = appObj.getLoginInfo();
    setAppLoginInformation(loginInfo);

    ... similarly appVersion
    setAppVersion(appVersion);
    ... similarly appPresident
    setAppPresident(appPresident);
}

Q3. Сравните код 1 и код 2, какой я должен использовать? Лично мне нравится код 1

Ответы [ 8 ]

15 голосов
/ 11 ноября 2009

Вам лучше определить Конструктор копирования и Оператор присваивания :

// Note the use of passing by const reference!  This avoids the overhead of copying the object in the function call.
MyApplicationCtx(const MyApplicationCtx& other);
MyApplicationCtx& operator = (const MyApplicationCtx& other);

Еще лучше, также определите частную структуру в вашем классе, которая выглядит следующим образом:

struct AppInfo
{
    std::string name;
    std::string path;
    std::string desciption;
    struct loginInformation loginInfo;
    int appVersion;
    std::string appPresident;
};

В конструкторе копирования и операторе присваивания класса вашего приложения вы можете воспользоваться автоматически сгенерированным в AppInfo оператором присваивания, чтобы выполнить все задания за вас. Предполагается, что вам нужно скопировать только подмножество MyApplicationCtx членов, когда вы «клонируете».

Это также будет автоматически корректно, если вы добавите или удалите элементы структуры AppInfo без необходимости переходить и менять весь шаблон.

2 голосов
/ 11 ноября 2009

Краткий ответ:

В1. Учитывая размер вашего класса MyAppCtx, да, если данные обрабатываются очень часто, произойдет значительное снижение производительности.

Q2: минимально, вы передаете указатель.

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

Длинный ответ: Предоставленные функции:

void FuncByValue(MyAppCtx ctx);
void FuncByRef1(MyAppCtx& ctx);
void FuncByRef2(MyAppCtx* ctx);

При передаче больших объектов, таких как MyApplicationCtx, рекомендуется использовать семантику ссылок (FuncByRef1 & FuncByRef2), передача по ссылке идентична по производительности передаче указателя, разница только в синтаксисе. Если вы передаете объект по значению, объект создается в функции копирования, так что аргумент, передаваемый в FuncByValue, отличается от параметра, который получает FuncByValue. Здесь вы должны быть осторожны с указателями (если они есть), содержащимися в объекте, который был передан по значению, потому что указатель также будет скопирован, поэтому очень возможно, что более одного объекта будут указывать на один элемент в памяти в определенный момент времени, что может привести к утечкам памяти, повреждению и т. д.

В целом, для таких объектов, как ваш MyAppCtx, я бы рекомендовал передавать по ссылке и использовать соответствующие методы доступа.

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

Дано (шаблон T используется просто для демонстрации того, что тип объекта здесь не имеет значения):

template<typename T>
void MyFunc(T myTobject);

При вызове MyFunc вы передаете аргумент, например:

int my_arg = 3;
MyFunc(my_arg);

И MyFunc получает параметр, например:

template<typename T>
void MyFunc(T myTobject)
{
  T cloned_param = T(myTobject);
}

Другими словами, my_arg - это аргумент, myTobject - это параметр.

Еще одно замечание: в приведенных выше примерах в памяти по существу присутствуют три версии my_arg: исходный аргумент, созданный при копировании параметр myTobject, плюс cloned_param, который также был явно скопирован.

1 голос
/ 11 ноября 2009

Общие сведения:

  • Зачем вам нужно копировать объект приложения? Не лучше ли для этого использовать синглтон (кстати, с полностью отключенным копированием)?

1:

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

2:

  • Передаваемая ссылка едва ли отличается (с точки зрения производительности) от передаваемого указателя, так что это в общем случае должно быть постоянной сложности. Это намного лучше. Не забудьте добавить модификатор "const", чтобы заблокировать модификации.

3:

  • Мне не нравятся оба из-за нарушения инкапсуляции. Однажды я увидел хорошую статью для программиста на Java, которая называлась что-то вроде «Почему сеттеры / геттеры - это зло» ( Ну, я нашел это легко, не так уж много основано на самой Java ). Это ОЧЕНЬ полезная статья, чтобы навсегда изменить стиль.
1 голос
/ 11 ноября 2009

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

0 голосов
/ 11 ноября 2009

Есть один пункт, который не был рассмотрен ни в одном из ответов (который был сосредоточен на ваших явных вопросах больше, чем в общем подходе). Я согласен с @luke в том, что вы должны использовать то, что идиоматично: конструктор копирования и операторы присваивания существуют по причине. Но только для обсуждения первой представленной вами возможности:

В первом блоке вы предлагаете небольшие функции, такие как:

public:
void setAppLoginInformation(MyApplicationCtx appObj){
    this->loginInfo = appObj.loginInfo; //assume it is correct
}

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

Если вы хотите предоставить метод только для установки одного из атрибутов, я бы изменил сигнатуру метода в соответствии с типом атрибута:

public:
void setAppLoginInformation( loginInformation const & li ); // struct is optional here

Это дает возможность изменить loginInformation как с полным контекстом приложения, так и с каким-то конкретным объектом информации для входа в систему, который вы можете создать самостоятельно.

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

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

0 голосов
/ 11 ноября 2009

Лучшая практика для клонирования объекта, где все члены копируются, как, кажется, делает «Код 1», это использовать конструктор копирования по умолчанию - вам вообще не нужно писать код. Просто скопируйте так:

MyApplicationCtx new_app = old_app;

«Код 2» делает что-то отличное от «Код 1», поэтому выбор одного над другим зависит от того, что вы хотите, чтобы код делал, а не от стиля.

Q1. Передача большого объекта по значению приведет к его копированию, что повлияет на производительность.

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

0 голосов
/ 11 ноября 2009

Q1 - да, каждый раз, когда вы передаете объект, копия делается

Q2 - минимальный, так как объект, переданный по ссылке, в основном просто указатель

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

0 голосов
/ 11 ноября 2009

Q1 ..

Передача объекта - сложная операция, так как он создает копию, вызывая конструктор копирования.

Q2.

Передайте ссылку на константу, это улучшит производительность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...