Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > стражи хидера не охраняют при инклуде!(


Автор: WiND 11.2.2010, 11:45
Сорри, заезжанная тема, но несмотря на то, что я прочитал много и все делаю по правилам,у меня не выходит)
к делу:
Файлы: Client.h/Client.cpp, MyFuncs.h, main.cpp
содержание:

Client.h:
Код

#pragma once
#include <windows.h>
#include <string.h>
#include "MyFuncs.h"

class CClient
{
public:
    CClient(void);
    ~CClient(void);
};


Client.cpp:
Код

#include "Client.h"

CClient::CClient(void)
{
}

CClient::~CClient(void)
{
}


MyFuncs.h:
Код

#pragma once
#include <string.h>
#include <Windows.h>

void StrToUpper(char *str)
{
    for (int i = 0; str[i] != NULL; i++)
    {
        str[i] = toupper(str[i]);
    }
}

int IndexOf(char *str, const char *substr)
{
    return (int)(strstr(str,substr)-str);
}


main.cpp:
Код

#pragma once
#include <windows.h>
#include "Client.h"
#include "MyFuncs.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpszCmdLine, int nCmdShow)
{
    char *str = "string";
    int i = IndexOf(str, "t");
    return 0;
}


даже в main.cpp добавил #pragma once )
пробовал и другой способ :#ifndef xxx #define xxx #endif
Ошибки:
Код

Error    2    error LNK2005: "int __cdecl IndexOf(char *,char const *)" (?IndexOf@@YAHPADPBD@Z) already defined in main.obj    Client.obj
Error    1    error LNK2005: "void __cdecl StrToUpper(char *)" (?StrToUpper@@YAXPAD@Z) already defined in main.obj    Client.obj
Error    3    fatal error LNK1169: one or more multiply defined symbols found    PATH\IncludeTester.exe


даже в main.cpp добавил #pragma once )
пробовал и другой способ :#ifndef xxx #define xxx #endif
Вот что не так?! Мне файл MyFuncs.h в дальнейшем почти везде понадобится, а подключить получается тольк однажды. будто не работает #ifndef и #pragma once

Автор: xvr 11.2.2010, 12:07
Это не стражи виноваты - они работают, иначе все развалилось бы еще на этапе компиляции, а не линковки. Это повторное определение функций (из .h) файла в нескольких единицах трансляции.
Добавь inline в их определение
Код

#pragma once
#include <string.h>
#include <Windows.h>
inline void StrToUpper(char *str)
{
    for (int i = 0; str[i] != NULL; i++)
    {
        str[i] = toupper(str[i]);
    }
}
inline int IndexOf(char *str, const char *substr)
{
    return (int)(strstr(str,substr)-str);
}

Автор: Alca 11.2.2010, 12:45
Глянь

Автор: Alca 11.2.2010, 12:46
Разбей свой MyFuncs.h на h-ник и cpp-шник

Автор: bilbobagginz 12.2.2010, 15:36
Цитата(WiND @  11.2.2010,  10:45 Найти цитируемый пост)
MyFuncs.h:

WiND
терминология:
файл .h - хедер (заголовок) - это место, где ты объявляешь классы, методы, и функции.
файл .cpp - исходник, это место, где ты реализуешь объявленное, определяешь.
единственное исключение - это inline функции/методы - когда ты их определяешь в заголовке.
напр. твои функции лучше всего объявить так:
Код

// filename: MyFuncs.h
#pragma once
#include <string.h>
#include <Windows.h>

void StrToUpper(char *str);
int IndexOf(char *str, const char *substr);

а определить так:
Код

// filename: MyFuncs.cpp
#include "MyFuncs.h"

void StrToUpper(char *str)
{
    for (int i = 0; str[i] != NULL; i++)
    {
        str[i] = toupper(str[i]);
    }
}
int IndexOf(char *str, const char *substr)
{
    return (int)(strstr(str,substr)-str);
}


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

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

А если функция - цикл (напр. strstr - обязательно цикл, и for(){} - тоже), то использовать inline - не стоит с т.з. скорости и величины программы.

но я - не гуру по C++, просьба гуру C++: 
если я не прав прошу меня ... поправить!

Автор: bsa 12.2.2010, 16:18
inline это рекомендация компилятору. Он может ее молча игнорировать. Что собственно и делает при отключенной оптимизации (а то невозможно отлаживать программу).
Нет ничего плохого в использовании циклов внутри inline-функций. Почти вся STL состоит из inline- функций/методов.
При вызове функций регистры не сохраняются. Просто эта операция не очень быстрая.
По умолчанию inline лучше использовать для функций типа геттеров и сеттеров, логика которых не предполагает сложных вычислений. Обычно, они возвращают/устанавливают значение переменной класса.
Так же без inline невозможно пока что делать шаблонные методы и функции. Так как далеко не все компиляторы поддерживают extern template - нельзя использовать шаблоны там, где не доступно их определение (реализация).
С другой стороны, как я уже написал выше, iniline - это только рекомендация. Компилятор, если сочтет нужным, сделает ее обычной функцией, которую будет вызывать при необходимости стандартным образом. Причем, эта функция будет единой для всех единиц трансляции (т.е. дублирования кода не будет).

Автор: bilbobagginz 12.2.2010, 16:59
 smile спасибки, bsa-джан.

Автор: WiND 12.2.2010, 17:22
Спасибо за ответы)
Т.е без разницы, использую я inline или нет? 
Как лучше поступить?
Так 
MyFuncs.h
Код

#pragma once
#include <string.h>
#include <Windows.h>
inline void StrToUpper(char *str)
{
    for (int i = 0; str[i] != NULL; i++)
    {
        str[i] = toupper(str[i]);
    }
}
inline int IndexOf(char *str, const char *substr)
{
    return (int)(strstr(str,substr)-str);
}


Или так

MyFuncs.h
Код

#pragma once
#include <string.h>
#include <Windows.h>

void StrToUpper(char *str);
int IndexOf(char *str, const char *substr);

MyFuncs.cpp
Код

#include "MyFuncs.h"
void StrToUpper(char *str)
{
    for (int i = 0; str[i] != NULL; i++)
    {
        str[i] = toupper(str[i]);
    }
}

int IndexOf(char *str, const char *substr)
{
    return (int)(strstr(str,substr)-str);
}


ЗЫ: Еще вопрос всплыл. 
Определяю макросы, где обычная конкатенация строковых литералов. с переменным числом параметров
#define MSG_1(param1, param2) "MSG1 " ## param1 ## " " ## param2
#define MSG_1(param1, param2б param3) "MSG2 " ## param1 ## " " ## param2 ## " " ## param3
...
Эти макросы будут часто использоваться, соответственно будет много констант с одним и тем же текстом. 
Думал заменить текст в макросах на константы, но это непозволительно.
Как лучше поступить? создать отдельную функцию?
или создать функцию, вызывающую эти макросы, тогда кол-во их будет одинарным? Отказаться вообще от макросов?
(Понимаю, дрянь эти макросы(дефайн и т.п) но почему-то меня к ним патологически тянет)

Автор: Alca 12.2.2010, 17:24
То что я предложил

Добавлено через 11 секунд
 smile 

Автор: WiND 12.2.2010, 17:35
2Alca, А чем будет лучше тогда? 
лично я не хочу из-зв двух(мб 3х) мизерный функций создавать еще срр файл. просто когда много файлов - путает сознание)
Но вместе с этим, хочется делать правильно) Несмотря на подробные ответы, они противоричивы и слегка сбили меня с толку) И так сходу я не понял разницы) вот и спрос ))

Автор: unicuum 12.2.2010, 17:46
Цитата(WiND @  12.2.2010,  17:22 Найти цитируемый пост)
Или так

Да, вот так.

Автор: Alca 12.2.2010, 17:54
Цитата

2Alca, А чем будет лучше тогда? 

раздутие кода 100%-во не будет

Автор: bilbobagginz 12.2.2010, 17:57
Цитата(Alca @  12.2.2010,  16:54 Найти цитируемый пост)
раздутие кода 100%-во не будет

раздутие какого кода?

Автор: Alca 12.2.2010, 17:58
Цитата

но почему-то меня к ним патологически тянет

а чем тебя функции не устроили?

Добавлено @ 17:59
Цитата

раздутие какого кода?

инлайного

Добавлено @ 18:00
Цитата

Т.е без разницы, использую я inline или нет? 

Если у него проект маленький, то пофиг что юзать.

Добавлено @ 18:03
Цитата

просто когда много файлов - путает сознание)

два вместо одного  smile

Добавлено @ 18:04
это у тебя тут 2-3 функции, а если 20-30? И всех их заинлайнить? Тогда возможно раздутие кода.

Добавлено @ 18:13
Если на то пошло, тогда пихай классы в один файл. Меньше файлов - меньше путаниц.  smile  

Автор: mes 12.2.2010, 18:28
Цитата(WiND @  12.2.2010,  16:22 Найти цитируемый пост)
Определяю макросы, где обычная конкатенация строковых литералов. с переменным числом параметров
Эти макросы будут часто использоваться, соответственно будет много констант с одним и тем же текстом. 

все одинаковые строковые литералы современный компилятор самостоятельно "объединяет", т.е. в теле программы будут определены единожды.


Автор: bsa 12.2.2010, 18:51
Цитата(Alca @  12.2.2010,  17:58 Найти цитируемый пост)
это у тебя тут 2-3 функции, а если 20-30? И всех их заинлайнить? Тогда возможно раздутие кода.
Раздутие будет только в том случае, если они реально "заинлайнятся".

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

Автор: WiND 12.2.2010, 18:51
Цитата

Если на то пошло, тогда пихай классы в один файл. Меньше файлов - меньше путаниц.    

Вот это уже не так. Классы пихать в один файл, практически всегда приносит больше путаницы.

Цитата

два вместо одного  

Если скажем 4 маленьких модуля по несколько функций, модули тематически разные, то 
получается уже не 2 вместо одного, а 8, вместо 4=)

Вот выдержка из какой-то учебной книжки:
Цитата

Встраиваемые (inline) функции – это очень короткие функции, реализуемые небольшим числом машинных команд. К ним невыгодно обращаться с использованием стандартного механизма, требующего обязательной засылки передаваемых аргументов в стек, извлечения данных из стека, засылки возвращаемого результата в стандартный регистр и т.п. Гораздо проще на место вызова inline-функции вставить и настроить само тело функции. Это намного эффективнее, особенно в тех случаях, когда работа функции сводится к нескольким машинным командам. Такая техника компиляции напоминает процедуру макроподстановки в ассемблере или процесс обработки препроцессором директив #define.


Возможно, я опять недопонял ) но если верить этому тексту, то bilbobagginz был прав в отношении подстановки. и раздутие кода походу тоже получается.
Но опять же, почти все стандартные хидеры что я видел, все из inline-ов. Не может же быть так, что по сути это макросы=) Тогда получается, что, как сказал bsa, 
компилятор сам решает когда ему использовать фун-ю как макрос, а когда, как функцию. Стало быть, как определить, когда он что делает, только дизасмить?

2mes, спасибо за ответ )

Автор: bsa 12.2.2010, 18:57
Цитата(WiND @  12.2.2010,  18:51 Найти цитируемый пост)
и раздутие кода походу тоже получается.

Цитата(WiND @  12.2.2010,  18:51 Найти цитируемый пост)
Тогда получается, что, как сказал bsa, 
компилятор сам решает когда ему использовать фун-ю как макрос, а когда, как функцию.

Оба утверждения верные. Когда происходит встраивание, код обычно увеличивается.

Автор: WiND 12.2.2010, 19:03
2mes, проверил в дизасме, оказывается он подставляет только полностью идентичные.
А в случаи, если параметры макроса отличаются, адреса тоже отличаются. Логично конечно почему так получается))
А сделать так, чтобы на название команды он ссылался на один адрес, а на параметры - другой адрес.
Хотя, я уже неуверен, что мне это нужно.
Возможно проще ввести одну функцию, принимающую сколь угодно параметров, а в качестве первого - название команды. и через свитч подставлять макросы) Но в таком случаи, я не уверен, что цель оправдает средства)

Автор: mes 12.2.2010, 19:04
Цитата(WiND @  12.2.2010,  17:51 Найти цитируемый пост)
Возможно, я опять недопонял ) 

inline-функция и функция_помеченная_как_inline   - разные вещи.
1. функция не обязательно должна быть помечена как inline чтоб стать inline-функцией
2. функция помеченная как inline не обязательно станет inline-функцией.

Цитата(WiND @  12.2.2010,  17:51 Найти цитируемый пост)
. Стало быть, как определить, когда он что делает, только дизасмить?

Это не нужно для преобладающего большинства задач.

Цитата(WiND @  12.2.2010,  17:51 Найти цитируемый пост)
Не может же быть так, что по сути это макросы=) 

вообще то не стоит сравнивать инлайн-функцию и макрос - общего в них мало.

smile

Автор: bilbobagginz 12.2.2010, 19:07
Цитата(WiND @  12.2.2010,  17:51 Найти цитируемый пост)
Возможно, я опять недопонял ) но если верить этому тексту, то bilbobagginz был прав в отношении подстановки

надо просто познакомиться с компилятором поближе.
в общем как и указал bsa, inline - это "предложение" компайлеру.
поэтому, все будет как я указал только если компайлер "решится, и пойдет на это".

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

Цитата(WiND @  12.2.2010,  17:51 Найти цитируемый пост)
Стало быть, как определить, когда он что делает, только дизасмить?

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

но в общем это надо почитать мануальчик какого-то конкретного компайлера.

Автор: mes 12.2.2010, 19:09
Цитата(WiND @  12.2.2010,  18:03 Найти цитируемый пост)
проверил в дизасме, оказывается он подставляет только полностью идентичные.

Ну так о том и говорилось smile

Цитата(WiND @  12.2.2010,  18:03 Найти цитируемый пост)
А сделать так, чтобы на название команды он ссылался на один адрес, а на параметры - другой адрес.

Ну тогда сделайте, чтоб было отдельно название, отдельно параметры smile 
у Вас же после препроцессора получается из параметров одна строка, которая и поступает компилятору.




Автор: WiND 12.2.2010, 19:10
Цитата

1. функция не обязательно должна быть помечена как inline чтоб стать inline-функцией

Т.е может получиться так, что какая-то гигантская функция, стала инлайн. Компилятор просто так захотел, а я ее определил в классе и не указывал inline?)

Добавлено через 5 минут и 49 секунд
Цитата

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

Тогда получится CONSTA+MACRO_COMMAND это не совсем красиво=)) Но в этом случаи придется уже использовать strcpy, а это еще лишний код) Если конечно я чего-то не знаю ))

Автор: mes 12.2.2010, 19:16
Цитата(WiND @  12.2.2010,  18:10 Найти цитируемый пост)
Т.е может получиться так, что какая-то гигантская функция, стала инлайн. Компилятор просто так захотел, а я ее определил в классе и не указывал inline?) 

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

Автор: bsa 12.2.2010, 20:58
Цитата(mes @  12.2.2010,  19:16 Найти цитируемый пост)
Практически - не может, так как большая функция состоит из множества команд, на фоне общего времени выполнения которых, время лишнего вызова незначительно.
А кто ему помешает, если функция больше нигде не используется?
Хотя, в любом случае все зависит от конкретного компилятора.

Добавлено через 1 минуту и 20 секунд

M
bsa
Завязываем с оффтопиком!
WiND, ответ по поводу стражей ты получил? Если да, то пометь тему решенной

Автор: xvr 12.2.2010, 21:14
 smile Когда компилятор решает сделать inline функции по своему усмотрению, то очень часто это может быть сделанно не из соображений экономии на вызове, а из соображений оптимизации. Подстановка функции в место вызова может позволить компилятору применить более агрессивную оптимизацию.
Пример (очень синтетический, но все же)
Код

#define BIG_CONST 10000
void func(int arr[BIG_CONST][BIG_CONST])
{
  for(int i=0;i<BIG_CONST;++i)
   func2(arr,i);
}

void func2(int arr[BIG_CONST][BIG_CONST], int idx)
{
 for(int i=0;i<BIG_CONST;++i)
  arr[i][idx]=0;
}
Данный код заполняет массив arr нулем по столбцам, что крайне неэффективно в смысле использования кэшей
Если компилятор заинлайнит func2 в func, то после этого он может поменять 2 for'а местами и сделать заполнение массива по строкам, что может быть на несколько порядков эффективнее

Автор: mes 12.2.2010, 21:26
Цитата(bsa @  12.2.2010,  19:58 Найти цитируемый пост)
А кто ему помешает, если функция больше нигде не используется?

никто, кроме разумного взгляда на необходимость такого решения.

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


Автор: unicuum 12.2.2010, 21:38
Цитата(mes @  12.2.2010,  18:28 Найти цитируемый пост)

все одинаковые строковые литералы современный компилятор самостоятельно "объединяет", т.е. в теле программы будут определены единожды.

А ещё написано, что полагаться на поведение компилятора нельзя, лучше следовать стандарту. На вопрос автора: макросы не рекомендуют использовать, если можно обойтись без них. В страже включения они оправданы вместе с директивой #pragma once. Для записи строковых литералов однозначно нет.

Автор: mes 12.2.2010, 22:25
Цитата(unicuum @  12.2.2010,  20:38 Найти цитируемый пост)

А ещё написано, что полагаться на поведение компилятора нельзя, лучше следовать стандарту.

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

Цитата(unicuum @  12.2.2010,  20:38 Найти цитируемый пост)
На вопрос автора: макросы не рекомендуют использовать, если можно обойтись без них.

Не совсем так. Не рекомендуется использовать в качестве функций. smile

Цитата(unicuum @  12.2.2010,  20:38 Найти цитируемый пост)
В страже включения они оправданы вместе с директивой #pragma once. 

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

Цитата(unicuum @  12.2.2010,  20:38 Найти цитируемый пост)
Для записи строковых литералов однозначно нет. 

какие Вы однoзначные.. a я бы с Вами не согласился smile

Добавлено через 54 секунды
насчет однозначности

Автор: WiND 13.2.2010, 23:29
Всем спасибо=) выполняю приказ bsa))

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