Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.141.46.108] |
|
Сообщ.
#1
,
|
|
|
В проекте файл cppProcFiles.cpp, который нормально компилируется. Я хотел бы научиться грамотно разбивать проекты на файлы и включать в файлы с исходным кодом заголовочные файлы, чтобы не было дублирования и конфликтов (чтобы в будущем не переделывать более сложные проекты, надеюсь, что до этого дорасту). Сделал файл cppTest.cpp с main и файл procfile.h с функциями обработки файлов. В результате получаю 27 ошибок. У меня возникли следующие вопросы:
1. Как грамотно включать в файлы заголовочные файлы, если они повторяются в каждом из файлов. В проекте только один файл cpp, но по мере освоения С++ проекты будут с несколькими файлами cpp. 2. Можно ли включать в заголовочные файлы и файлы cpp строки namespace fs = filesystem; using namespace std; одновременно или нужно в заголовочных файлах все включать полностью. 3. Какие ошибки я сделал при создании проекта с несколькими файлами. 4. Посоветуйте, пожалуйста, источники, в которых хорошо изложены принципы построения проектов C++ и есть ответы на эти вопросы. Прикреплённый файлcppTest.zip (4,34 Кбайт, скачиваний: 44) |
Сообщ.
#2
,
|
|
|
Мы начинаем так
#ifndef NOOB_ONLINE #define NOOB_ONLINE class Class1{ }; #endif |
Сообщ.
#3
,
|
|
|
Есть еще
#pragma once |
Сообщ.
#4
,
|
|
|
|
Сообщ.
#5
,
|
|
|
Qraizer покритикуйте мой проект
Правила по которым я строю проект 1. Файлы идут парами : имяфайла.h имяфайла.cpp 2. В файле Project.cpp находится точка входа WinMain() и главная процедура WndProc() и все. 3. В файле .cpp находятся только функции в алфавитном порядке. 4. В файле .h находятся все глобальные переменные, вкладываю в namespace e{} тогда в любом месте глобальная переменная будет выглядеть e::имяпеременной. 5. Не люблю подчеркивания поэтому <имя_переменной> не делаю, практика показывает что <имяпеременной> читается не хуже, но зато проект не рябит подчеркиваниями и не создает визуальный шум. 6. Объявление переменных всегда пишу в начале функции int i,j,k. 7. Функции ВерблюжьяЗапись() причем первая чать имя модуля где расположена функция. 8. Глобальные переменные по названию либо должны точно определять ее назначение, либо иметь в названии чать модуля где они более всего используются 9. Обычно еще есть модуль Temp.h в который как в записную книжку сбрасываю временную информацию, периодически оттуда все удаляю 10.Комментарии не пишу* Скрытый текст * Комментарий это как goto если они есть значит функция не доработана и ее можно сделать лучше. Комментарий у меня только в начале модуля чтобы знать что это за модуль - если придется напечатать или что-то еще. Комментарии удобно использовать в модуле над которым ведется работа - копируешь блок закомментировал переделываешь под свое, либо что-то нужно временно выключить. Либо найти вхождение глобальной переменной в любом месте программы - закомментировал в объявлении и пожалуйста компилятор как ошибки укажет все вхождения глобальной переменной. |
Сообщ.
#6
,
|
|
|
Цитата Betelgeuse @ Прикреплённый файлProgram_graphics.rar (65,07 Кбайт, скачиваний: 1) Что-то мутное! Virustotal на Program.exe: APEX 6.434 | Malicious BitDefenderTheta 7.2.37796.0 | Gen:NN.ZexaE.36318.bqW@ayRumtni Bkav 2.0.0.1 | W32.AIDetectMalware MaxSecure 1.0.0.1 | Trojan.Malware.300983.susgen Trapmine 4.0.14.90 | malicious.moderate.ml.score |
Сообщ.
#7
,
|
|
|
Если программа с вирусами, значит не нужно ее запускать. Вы совершенно правы
|
Сообщ.
#8
,
|
|
|
Была дурацкая ошибка: using namespace std; после namespace fs = filesystem; Из-за этого столько ошибок возникало. Программа после исправления ошибки заработала. Спасибо всем ответившим. Мой вопрос был преждевременным, поскольку в проекте не было нескольких файлов, а только один заголовочный и один файл исходного кода. Посмотрю рекомендации в книгах по C++.
|
Сообщ.
#9
,
|
|
|
Цитата Betelgeuse @ покритикуйте мой проект Утечка ресурса - использована функция RegisterClassA, не использована функция UnregisterClassA. --- Цитата 2. В файле Project.cpp находится точка входа WinMain() я бы назвал этот файл WinMain.cpp И далее так по всем классам - имя класса (или имя процедуры) это имена *.h и *.cpp файлов --- Класс - сабклассинг: Скрытый текст // h-файл // -------------------------------------------------------------------------- // FILE MSubClass.h 21.08.2006 // -------------------------------------------------------------------------- // класс - саб-классинг // Дает возможность выполнять процедуру как до вызова оригинальной // оконной функции, так и после в том числе и оба варианта сразу. // -------------------------------------------------------------------------- #ifndef __MSubClass__H #define __MSubClass__H // -------------------------------------------------------------------------- class MSubClass { public: static const int SUBCLASS_NO_DIALOG = 0; static const int SUBCLASS____DIALOG = 1; private: static LRESULT CALLBACK SubClassStatic (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); protected: virtual int WINAPI SubClass (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); virtual void WINAPI SubClassPast (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int ReturnCode; int index; HWND hwndSub; WNDPROC winrou; DWORD err; LONG_PTR OldUserData; LRESULT RetC; public: WINAPI MSubClass (); virtual WINAPI ~MSubClass (); bool WINAPI InstallSub (HWND hwndSub_, int TipeSub=SUBCLASS_NO_DIALOG); DWORD WINAPI LastError(void){return err;} }; // -------------------------------------------------------------------------- // пример сабклассинга окна редактирования: // /* #define IDCONEDITTEST 0xFFF4 MSubClass *pSC=NULL; HWND hConTest; hConTest = ::CreateWindowEx ( WS_EX_OVERLAPPEDWINDOW | WS_EX_STATICEDGE, "edit", NULL, WS_CHILD | ES_LEFT | WS_VISIBLE, x+10, y+40, 60, 30, GetHWND(),//Dlghwnd, (HMENU)IDCONEDITTEST, hInst, NULL ); pSC = new MSubClass ();pSC->InstallSub (hConTest); .. .. delete pSC; pSC=NULL; */ // // -------------------------------------------------------------------------- #endif // cpp файл // -------------------------------------------------------------------------- // FILE MSubClass.cpp 21.08.2006 // -------------------------------------------------------------------------- #include "stdafx.h" #include "MSubClass.h" // -------------------------------------------------------------------------- WINAPI MSubClass::~MSubClass () { if (hwndSub != NULL) { // попытка сделать это для уже разрушенного окна // не приведет к исключению. // Просто будет выдана ошибка и фсе. ::SetWindowLongPtr (hwndSub,GWLP_USERDATA,OldUserData); ::SetWindowLongPtr (hwndSub,index,(LONG_PTR)winrou); } } // -------------------------------------------------------------------------- WINAPI MSubClass::MSubClass () {hwndSub=NULL;ReturnCode=0;} // -------------------------------------------------------------------------- bool WINAPI MSubClass::InstallSub (HWND hwndSub_, int TipeSub) { bool Result=true; if (hwndSub == NULL) { hwndSub = hwndSub_; if (TipeSub == SUBCLASS_NO_DIALOG) {index = GWLP_WNDPROC;} else {index = DWLP_DLGPROC;} winrou = (WNDPROC)::GetWindowLongPtr(hwndSub,index); // спасем старый параметр, который может быть нужен окошку OldUserData = ::GetWindowLongPtr(hwndSub,GWLP_USERDATA); ::SetWindowLongPtr (hwndSub,GWLP_USERDATA,(LONG_PTR)this); RetC=::SetWindowLongPtr (hwndSub,index,(LONG_PTR)SubClassStatic); if(RetC==0) { err = ::GetLastError(); if (err != NO_ERROR) Result=false; } } else { Result=false; } return Result; } // -------------------------------------------------------------------------- LRESULT CALLBACK MSubClass::SubClassStatic (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { MSubClass *pSC; int retc=10; pSC = (MSubClass *) ::GetWindowLongPtr (hwnd,GWLP_USERDATA); if (!pSC) return ::DefWindowProc(hwnd, message, wParam, lParam); if (hwnd == pSC->hwndSub) { retc =pSC->SubClass(hwnd,message,wParam,lParam); } switch (retc) { case 0: pSC->RetC = ::CallWindowProc(pSC->winrou, hwnd, message, wParam, lParam); pSC->SubClassPast(hwnd,message,wParam,lParam); return pSC->RetC; case 1: return pSC->ReturnCode; default: return ::DefWindowProc(hwnd, message, wParam, lParam); } } // -------------------------------------------------------------------------- int WINAPI MSubClass::SubClass (HWND , UINT , WPARAM , LPARAM ) { return 0; } // -------------------------------------------------------------------------- // при необходимости может исказить RetC void WINAPI MSubClass::SubClassPast (HWND , UINT , WPARAM , LPARAM ) { } // -------------------------------------------------------------------------- |
Сообщ.
#10
,
|
|
|
Цитата ЫукпШ @ Утечка ресурса - использована функция RegisterClassA, не использована функция UnregisterClassA. За подсказку по сабклассингу - спасибо ) |
Сообщ.
#11
,
|
|
|
Добавлено Цитата Betelgeuse @ В общем случае это невозможно, т.к. методы хотят this, который при & над методом (или элементом данных) класса встраивается в сам тип указателя, что приводит к несовместимости типов указателей на элементы класса и указателей на обычные сущности. Так для int A::f() тип указателя будет int (A::*)(), а не int (*)(). И операция разыменования указателя не просто *, а .* или ->*, где слева указывается экземпляр класса. Эти операции позволяют этот экземпляр использовать как конкретный this для указателя. Понятно, что * такого не умеет. Единственный вариант, сохраняющий совместимость типов указателей – это использовать статические методы (или данные), но и тут подводные камни в лице невозможности из таких методов обращаться к нестатическим полям и методам. Причина опять же в том, что неоткуда брать конкретный this.Проблема вот в чем : когда есть CALLBACK функции, особенно субклассинг - как их всунуть в класс ? Но в частных случаях этот this можно куда-нибудь замешать. Многие callback в WinAPI предусматривают дополнительный параметр, который сам WinAPI игнорит, но передаёт в неизменном виде в сам callback. Его легко можно использовать для такого замешивания. Типа такого: class WindowEnumerator { virtual bool currentWindow(HWND) = 0; public: static BOOL CALLBACK enumerator(HWND hWnd, LPARAM param) { return reinterpret_cast<WindowEnumerator*>(param)->currentWindow(hWnd); } }; class WindowEnumerator_impl: public WindowEnumerator { virtual bool currentWindow(HWND hWnd) { std::cout << "Found " << hWnd << std::endl; return true; } }; /* ... */ EnumWindows(&WindowEnumerator::enumerator, reinterpret_cast<LPARAM>(&WindowEnumerator_impl())); Добавлено В твоём случае, когда тебе нужна конкретно WNDPROC, можно ещё проще. Зарезервируй в окне дополнительное место под this, запиши его туда SetWindowLongPtr() и читай GetWindowLongPtr() в WNDPROC |
Сообщ.
#12
,
|
|
|
Разбил проект на несколько файлов. В Visual Studio 2022 проект компилируется без ошибок. А в Visual Studio Code при компиляции его с помощью gcс получаю ошибку
D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\Admin\AppData\Local\Temp\cc9YPIRm.o: in function `main': D:/MyProgramming/cppProcFiles/cppProcFiles.cpp:42: undefined reference to `CreateVectorsFiles(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, char const*, char const*)' collect2.exe: error: ld returned 1 exit status Прикреплённый файлcppProcFiles.zip (5,5 Кбайт, скачиваний: 44) |
Сообщ.
#13
,
|
|
|
Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h
|
Сообщ.
#14
,
|
|
|
Цитата Qraizer @ Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл cppProcFiles.h не надо. Видимо, препроцессор Visual Studio так и понимает, а Visual Studio Code понимает по-другому. Эти ошибки исчезли, но появились другие:Твой интерфейс cppProcFiles.h зависит от std::vector<> и std::string, значит должен включить их интерфейсы. Добавь подключение <vector> и <string> в cppProcFiles.h D:\msys64\mingw64\bin\g++.exe -fdiagnostics-color=always -g D:\MyProgramming\cppProcFiles\cppFun.cpp -o D:\MyProgramming\cppProcFiles\cppFun.exe D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-crtexewin.o): in function `main': C:/M/B/src/mingw-w64/mingw-w64-crt/crt/crtexewin.c:67: undefined reference to `WinMain' collect2.exe: error: ld returned 1 exit status |
Сообщ.
#15
,
|
|
|
Цитата tumanovalex @ Рассчитывать, что вокруг тебя создадут требуемое тебе операционное окружение – это неверный подход. И неудобный. Декларируемый интерфейс должен быть самодостаточным и не зависеть от окружения, в котором используется. Если он зависит от других подсистем, будет более чем естественно, если он явно это покажет, сделав подключения необходимых интерфейсов прямо в декларации своего. Иначе как я, будучи её пользователем, смогу понять, что за сущности в нём фигурируют? И более того, я могу вообще не заглядывать вовнутрь cppProcFiles.h и оказаться сильно удивлён при попытке его использовать. Вот как ты сейчас.Я думал, что предпроцессор подставит в cppProcFiles.cpp файл cppProcFiles.h, тогда в объединенном файле уже будет указание на включаемые файлы <vector> и <string> и включать их в файл cppProcFiles.h не надо. Это не значит, что в .h должен фигурировать полный набор, требуемый для реализации. Например, <string.h> никуда не денется от <malloc.h>, т.к. в его составе есть функции, самостоятельно создающие новые ASCIIZ-строки, однако видеть #include <malloc.h> внутри string.h совсем необязательно. Вот какой-нибудь string.с, реализующий интерфейс <string.h>, будет его иметь, но заявлять об этом в декларации самого <string.h> нет необходимости, это никак не повлияет на понимание его контента. А вот увидеть <stddef.h> будет вполне ожидаемым, т.к. вдруг такая функции inline и возвращают NULL, а определён он именно там. То же можно сказать и о пользователе. Он не обязан знать, какие подсистемы используются в декларации, а какие в реализации используемого им интерфейса. Он просто сделал его #include и хочет его использовать. Если ему в программе требуется <vector>, то он не должен выискивать .h, который его подключает или нет, чтобы понять, а нужно ли ему его подключать. Нужно. Раз использует, подключает и не парится. Если <vector> также подключается где-то ещё, пользователя это не должно заботить. Другими словами, самодостаточность зависимых интерфейсов обоюдна: и декларируемый интерфейс не должен зависеть от окружения, и окружение не должно полагаться на внутренние зависимости подсистем. Добавлено Цитата tumanovalex @ Там какого-то ключика линкеру не хватает, чтоб знал, какой стартовый .o брать. Не могу сказать какого, мингвы счас не стоит нигде. Студия в этом смысле несколько умнее, что сама видит, то линкеру и говорит, но так не всегда было, раньше тоже приходилось ключиком разъяснять, что за приложение я собираю. Откуда появилась ссылка на WinMain в консольном приложение - не понятно. |
Сообщ.
#16
,
|
|
|
Спасибо за ответ и подробные объяснения. По поводу gcc спрошу в кроссплатформенной теме.
|
Сообщ.
#17
,
|
|
|
Цитата Qraizer @ Там какого-то ключика линкеру не хватает, чтоб знал, какой стартовый .o брать. Не могу сказать какого, мингвы счас не стоит нигде. Студия в этом смысле несколько умнее, что сама видит, то линкеру и говорит, но так не всегда было, раньше тоже приходилось ключиком разъяснять, что за приложение я собираю. тут нужные ключики |
Сообщ.
#18
,
|
|
|
Цитата Majestio @ Спасибо! Ошибка исчезла. Но, как я понимаю, проблема в том, что в параметре tasks.json нужно указать, что нужно компилировать не один, а несколько файлов. Буду разбираться с структурой этого файла и попытаюсь исправить его так, чтобы компилировался весь проект, а не один файл. тут нужные ключики |