Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > 2 вида функций const и обычная.


Автор: Rapalex 10.8.2007, 12:00
У одного класса есть 2 функции:
Код


CGsr* CProject::GetGsr(const CSourceType *poSourceType)
{
    for (INT_PTR i=0; i<m_arPtrGsr.GetSize(); i++)
    {
        CGsr *poGSR = m_arPtrGsr.GetAt(i);
        if (*poSourceType == *poGSR)
            return poGSR;
    }
    return NULL;
}

const CGsr* CProject::GetGsr(const CSourceType *poSourceType) const
{
    for (INT_PTR i=0; i<m_arPtrGsr.GetSize(); i++)
    {
        CGsr *poGSR = m_arPtrGsr.GetAt(i);
        if (*poSourceType == *poGSR)
            return poGSR;
    }
    return NULL;
}


Разница между ними только в том что одна из них константная. Когда это функции в одну строчку типа:
Код

INT_PTR CProject::AddGsr(Gsr::CGsr *poGsr)
    { return m_arPtrGsr.Add(poGsr); }

я спокоен, а как в таком случае можно избавится от дублирования кода?

Автор: Lazin 10.8.2007, 12:09
если есть возможность сделать ф-ю константной, то неконстантная обычно не нужна, так как константная будет работать и для неконстантных аргументов.  smile Ну и фраза млин. 
Короче константная канает везде а обычная не везде.

Автор: Vyacheslav 10.8.2007, 12:54
Написать еще одну 
Код

const CGsr* CProject::InternalGetGsr(const CSourceType *poSourceType) const
{
   for (INT_PTR i=0; i<m_arPtrGsr.GetSize(); i++)
    {
        CGsr *poGSR = m_arPtrGsr.GetAt(i);
        if (*poSourceType == *poGSR)
            return poGSR;
    }
    return NULL;

}

CGsr* CProject::GetGsr(const CSourceType *poSourceType)
{
    return const_cast<CGsr*>( InternalGetGsr(poSourceType));
}

const CGsr* CProject::GetGsr(const CSourceType *poSourceType) const
{
   return InternalGetGsr(poSourceType);
}



PS Целесобразность присутствия в классе двух таких функций оставляем вне рамок  данного обсуждения smile

Автор: Lazin 10.8.2007, 13:25
Вообще нужно либо делать ф-ии константными везде где это можно, либо вообще их не использовать, но в этом случае константными объектами будет проблематично пользоваться. 

Автор: Vyacheslav 10.8.2007, 14:15
В данном случае, насколько я понял, константность функции нужна, что бы иметь перeгрузку функций,  формально отличающихся только возращаемым типом 
Код

CGsr* GetGsr(const CSourceType *poSourceType);
const CGsr*GetGsr(const CSourceType *poSourceType) const

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

Автор: Rapalex 10.8.2007, 14:54
Vyacheslav, константная функция будут вызыватся если я работаю с константным указателем, а если указатель не константный, то вызовется не константная функция.

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

Автор: Vyacheslav 10.8.2007, 15:51
Вот это в данной формулировке не совсем корректно
Цитата(Rapalex @  10.8.2007,  14:54 Найти цитируемый пост)
Vyacheslav, константная функция будут вызыватся если я работаю с константным указателем, а если указатель не константный, то вызовется не константная функция.

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

CProject project;
//....
const CGsr* pGsr = project.GetGsr(poSourceType);  

вызовется неконстантный вариант

Код

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

А вот это , скажем так, понятно но не черезчур Вы себя контролируете? Константный или неконстантный объект - это уже известно на этапе компиляции, если объект  константный  а функции констатной нет, то  Вы сами можете написать 
Код

void SomeFuction (const CProject* project )
{
//...
const CGsr* pGsr = project->GetGsr(poSourceType); 
//...
}
 

И фиг Вам дальше компилятор позволят изменить pGsr
Единственное отличие, что в случае наличия константной функции Вас компилятор обругает, если Вы случайно ошибетесь и напишите
Код

void SomeFuction (const CProject* project )
{
//...
CGsr* pGsr = project->GetGsr(poSourceType); 
//...
}
 

Ради  одного этого такой огород городить? Неужели не в состоянии сами себя проконтролировать. Все таки это не тот  случай, когда в аналогичном случае в STL возвращается iterator и const_iterator ( const_iterator != const iterator  smile  )

Единственная польза от такого приема в конкретном случае мне видится только одна: если Вы любитель "многоуровневых" обращений
Код

project->GetGsr(poSourceType)->SomeFunction();


 


Автор: Ln78 10.8.2007, 16:00
Хороший вопрос. Я сам раньше дублировал, сейчас попробовал несколько изменённый пример 
Vyacheslavа, получилось (по крайней мере в VS2005):
Код

class CZ 
{
    int z;
public:
    CZ() {z=111;}
    int& GetZ(void) 
    {
        return z;
    } 
    const inline int& GetZ(void) const
    {
        return (const_cast<CZ&> (*this)).GetZ();
    } 
};

void F(const CZ& cz1, CZ& cz2)
{
    int q = 2*cz1.GetZ();
    cz2.GetZ() = 13 + q;
//    cz1.GetZ() = 100; //Даёт ошибку компиляции, как, собственно, и хотели
}



Цитата(Vyacheslav @  10.8.2007,  15:51 Найти цитируемый пост)
Неужели ради  этого такой огород городить

ИМХО, часто стоит: например, для контейнеров, у которых перегружаются операторы [] для доступа к элементам это бывает очень полезно

Да, кстати, Rapalex, для соседнего топика: например, в книге Элджера варианты константных указателей и указателей на константу расписаны

Автор: Vyacheslav 10.8.2007, 16:14
Цитата(Ln78 @  10.8.2007,  16:00 Найти цитируемый пост)
ИМХО, часто стоит: например, для контейнеров, у которых перегружаются операторы [] для доступа к элементам это бывает очень полезно

ЭЭЭ. Нельзя же разумную идею превращать в ее противоположность. Если бы это был оператор[], то я бы не имел ничего против, поскольку для оператор [] естественно использование типа  
Код

project[poSourceType] = cGsr;
project[poSourceType] .SomeFuction();

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

Автор: Ln78 10.8.2007, 16:23
Я имел в виду: основная идея - избежать дублирования фукнций (и операторов в том числе), а уж к чему её применить, это уже другой вопрос  smile 

Автор: Alek86 10.8.2007, 17:43
Vyacheslav, как я понимаю, это требуется (и у меня нередко требовалось) в случае, если нужно:

1)
CGsr* GetGsr(const CSourceType *poSourceType);

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

2)
const CGsr*GetGsr(const CSourceType *poSourceType) const;

Получать указатель на внутренние данные с целью их "чтения" в других константных функциях.

Ну, и еще можно не добавлять 3ю функцию, а вызвать в неконстантной константную через
((const CProject*) this)->...
Но это дело вкуса, как я понимаю....


Если я не прав, просьба не нервничать. smile

Автор: Rapalex 11.8.2007, 09:45
Ln78, 
Цитата(Ln78 @  10.8.2007,  16:00 Найти цитируемый пост)
 для соседнего топика: например, в книге Элджера варианты константных указателей и указателей на константу расписаны

А можно полне название книги, чтоб погуглить смог  smile 

Alek86, полность согласен, особенно с 
Цитата(Alek86 @  10.8.2007,  17:43 Найти цитируемый пост)
Получать указатель на внутренние данные с целью их "чтения" в других константных функциях.


Автор: Ln78 11.8.2007, 11:33
Rapalex, книга из серии "Библиотека программиста", автор Джеф Элджер, название "С++". У меня только бумажная версия.
И кстати, почему согласен только с Alek86? В моём примере как раз это и приведено, так же, как и отсутствие промежуточной функции.  smile  Там просто не указатели а ссылки, но это дело не меняет.

Автор: Alek86 11.8.2007, 11:50
Ln78, в твоем примере

Код

    const inline int& GetZ(void) const
    {
        return (const_cast<CZ&> (*this)).GetZ();
    } 


константная функция вызывает неконстантную?

Автор: Ln78 11.8.2007, 14:53
Alek86, а сам как думаешь? Скопируй пример, дополни вызовом функции F и посмотри под отладчиком, кто что вызывает. Ну а сколько в моём примере функций вроде и так видно. Чтобы не повышать мелкодисперсность оксида двухатомного водорода механическим путём (воду в ступе толочь) вряд ли нужно обсуждать это ещё на несколько постов.

Автор: Alek86 11.8.2007, 15:59
проверил, работает...
странно, я всегда считал, что внутри константных функций неконстантные вызывать нельзя...

добавлено:

понял уже. хитрый прием, не знал. теперь тему можно закрывать

Автор: Lazin 11.8.2007, 20:32
Цитата

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

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

Автор: Rapalex 23.5.2008, 13:15
Всем спасибо.

Автор: Alek86 23.5.2008, 16:32
 smile 
 smile 

Автор: d06osipov 23.5.2008, 18:43
Зато так вы не сможете избежать ошибок в самой процедуре. Если вы используете const_cast информация о константности теряется и компилятор уже не может проследить, чтобы ваша функция не модифицировала класс.

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

Я бы на вашем месте не использовал const_cast без необходимости. Лучше всего, мне кажется дублирование кода, но если очень не хочется, можно обратится к препроцессору, например так:
Код

#define CONST_PROC(head,body) head body const head const body


И использовать это так:
Код

CONST_PROC(inline int& GetZ(void),\
{\
    return (const_cast<CZ&> (*this)).GetZ();\
} \
)

хотя тут тоже уйма недостатков

Автор: Alek86 23.5.2008, 19:18
да уж
прошел чуть ли не год и я тоже уже думаю, что лучше мелкий копипаст

вот так вот меняются взгляды... )

Автор: vinter 23.5.2008, 22:32
Цитата(d06osipov @  23.5.2008,  19:43 Найти цитируемый пост)
Лучше всего, мне кажется дублирование кода, но если очень не хочется, можно обратится к препроцессору, например так:

сколько людей, столько мнений. Я считаю что макросы - зло.

Автор: UnrealMan 24.5.2008, 12:08
Цитата(d06osipov @  23.5.2008,  18:43 Найти цитируемый пост)
Зато так вы не сможете избежать ошибок в самой процедуре. Если вы используете const_cast информация о константности теряется и компилятор уже не может проследить, чтобы ваша функция не модифицировала класс.

Достаточно выражать неконстантную функцию через константную, и таких ошибок не будет.

Цитата(d06osipov @  23.5.2008,  18:43 Найти цитируемый пост)
но если очень не хочется, можно обратится к препроцессору, например так

Мсье знает толк в извращениях. При юзании твоего макроса как минимум появится проблема с использованием запятых в теле функции.

Автор: JackYF 25.5.2008, 01:02
Цитата(UnrealMan @  24.5.2008,  11:08 Найти цитируемый пост)
Достаточно выражать неконстантную функцию через константную, и таких ошибок не будет.

Если константная функция вернёт мне const T, то мне ничего не остаётся, как сделать const_cast<T&>(...), чтобы вернуть то, что должна возвратить неконстантная функция. Другое дело, что это как раз очень безобидное применение const_cast.

Автор: UnrealMan 25.5.2008, 01:47
Цитата(JackYF @  25.5.2008,  01:02 Найти цитируемый пост)
это как раз очень безобидное применение const_cast. 

Дык об этом и речь. Некоторые люди очень любят катить совершенно пустые бочки на const_cast, reinterpret_cast, макросы и т.д.

Автор: korian 20.3.2009, 13:16
интересует мнение по поводу такой реализации const / не const геттеров, чтобы избежать дублирования кода.
(мнения тех, кто считает std::vector ошибкой проектирования не интересуют)
Код

class MyClass
{
public:
   SomeData *GetValue() {return GetValueImpl<MyClass, SomeData>(*this);}
   const SomeData *GetValue() const {return GetValueImpl<const MyClass, const SomeData>(*this);}

private:
   template<class SelfType, class ResultType>
   inline static ResultType *GetValueImpl(SelfType &self)
   {
        //реализация
   }
}

Автор: mes 20.3.2009, 14:19
Цитата(korian @  20.3.2009,  12:16 Найти цитируемый пост)
интересует мнение по поводу такой реализации const / не const геттеров, чтобы избежать дублирования кода.

имхо извращение smile

Автор: korian 20.3.2009, 14:28
ну
Код

  const SomeData *GetValue() const {return const_cast<MyClass *>(this)->GetValue();}
имху тоже извращение  smile 
вопрос какой извращение лучше

Автор: mes 20.3.2009, 15:20
Цитата(korian @  20.3.2009,  13:28 Найти цитируемый пост)


Код

  const SomeData *GetValue() const {return const_cast<MyClass *>(this)->GetValue();}

имху тоже извращение  smile 
вопрос какой извращение лучше

имхо с const_cast, только в обратном виде, чем в цитате.

Автор: korian 20.3.2009, 19:53
Цитата(mes @  20.3.2009,  14:20 Найти цитируемый пост)
имхо с const_cast, только в обратном виде, чем в цитате

в обратном виде - это так?
Код

SomeData *GetValue()
{
   const MyClass &self = *this;
   return const_cast<SomeData *>(self.GetValue());
}

а чем так лучше?

Автор: mes 20.3.2009, 20:39
Цитата(korian @  20.3.2009,  18:53 Найти цитируемый пост)

в обратном виде - это так?

ага
Цитата(korian @  20.3.2009,  18:53 Найти цитируемый пост)
а чем так лучше? 

вместо объяснения лучше покажу примером :

Код

typedef int some;

class myclass
{
    public:

        const some* get1 () const { return const_cast<const some*>( const_cast<myclass*>(this)->get1()); }
              some* get1 ()  { ++m_i; return &m_i; }  // <<---

        const some* get2 () const { return &m_i; }
              some* get2 ()  { return const_cast<some*>( const_cast<const myclass*>(this)->get2()); }

    private:
        some m_i;
};



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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)