
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.60] |
![]() |
|
Страницы: (6) « Первая ... 4 5 [6] все ( Перейти к последнему сообщению ) |
Сообщ.
#76
,
|
|
|
У вас проблема в том что вы и в заголовок и в cpp файл впихнули одно и то же.
Вы уж определитесь нужен ли вам в данном случае вообще заголовок. Если больше никто этот ваш код использовть не будет и нигде кроме этого файла эти классы не понадобятся - то да, можно и так. Если же вы хотите написать нормально и не копировать код в другом файле - то в заголовок размещается объявление классов, функций, дефайнов (если они влияют на несколько единиц трансляции), а вся реализация - выносится в cpp файл. |
![]() |
|
|
Цитата sharky72 @ ... Если же вы хотите написать нормально и не копировать код в другом файле - то в заголовок размещается объявление классов, функций, дефайнов (если они влияют на несколько единиц трансляции), а вся реализация - выносится в cpp файл. Я так и хочу сделать, заголовки разместить в h-файле, а реализацию в cpp-файл. Но проблема в том, что я не понимаю как в данном случае вынести заголовки. |
Сообщ.
#78
,
|
|
|
Вы понимаете разницу между объявлением и реализацией?
Я же вам привел пример объявления класса в .h и реализации его методов в .cpp Как исправить ошибку: "LNK2019 ссылка на неразрешенный внешний символ"? Ну почитайте хотя бы основы C++ https://www.learncpp.com/cpp-tutorial/class...d-header-files/ |
![]() |
|
|
Я отредактировал код, но одна ошибка осталась: идентификатор "run" не определен.
Подскажите, пожалуйста, как исправить ошибку? Файл дикларации: ![]() ![]() #pragma once #include <iostream> #include <sstream> #include <string> #include <vector> #include <locale> namespace school { #define l(v) std::wcout << #v << " = " << (v) << "\n"; // Класс-обертка для преобразования в строку class StringConverter { public: template<typename T> static std::wstring toString(T val) { return (std::wostringstream() << val).str(); } }; // Класс для работы со строками class MyString : public std::wstring { public: MyString(); MyString(std::wstring s); MyString(const wchar_t* s); template<typename T> MyString& operator=(const T a) { return *this = StringConverter::toString(a); }; }; // Класс для работы с вектором template<typename T> class MyVector : public std::vector<T> { public: T& operator[](const size_t i) { if (i >= this->size()) this->resize(i + 1); return *(this->begin() + i); } void resize(size_t new_size, const T& value = T()) { std::vector<T>::resize(new_size, value); } }; // Класс для форматирования таблицы class TableFormatter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void format(); private: void calculateMaxColumns(); void resizeRows(); void addRowNumbers(); void calculateColumnWidths(); void alignColumns(); // Заглушка для совместимости с логикой void alignHeaders(); // Заглушка для совместимости с логикой }; // Класс для вывода таблицы class TablePrinter { private: MyVector<MyVector<MyString>>& m; MyVector<MyString>& h; size_t& CMAX; public: TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header); void print(std::wostream& o); private: std::wstring createLine(); void printHeader(std::wostream& o); void printRows(std::wostream& o); }; // Основной класс таблицы class Tab { private: MyVector<MyVector<MyString>> m; MyVector<MyString> h; size_t CMAX = 0; public: // Tab() = default Tab(); // объявление оператора[] MyVector<MyString>& operator[](const size_t i); template<typename ... T> void head(T ... t) { (h.push_back(StringConverter::toString(t)), ...); } void formater(); // Объявляем дружественные классы friend class TableFormatter; friend class TablePrinter; friend std::wostream& operator<<(std::wostream& o, Tab& t); }; std::wostream& operator<<(std::wostream& o, Tab& t); ///----------------------------------------------------------------------------| /// Тест. ///----------------------------------------------------------------------------: class TabTest { public: void run(); }; } // namespace school Файл реализации: ![]() ![]() #include "DataTable.h" namespace school { // Реализация методов MyString MyString::MyString() : std::wstring() {} MyString::MyString(std::wstring s) : std::wstring(s) {} MyString::MyString(const wchar_t* s) : std::wstring(s) {} // Явная инстанциация шаблонов template class MyVector<MyString>; template class MyVector<MyVector<MyString>>; // Реализация TableFormatter TableFormatter::TableFormatter(size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : m(data), h(header), CMAX(cmax) { } void TableFormatter::format() { calculateMaxColumns(); resizeRows(); addRowNumbers(); calculateColumnWidths(); alignColumns(); alignHeaders(); } void TableFormatter::calculateMaxColumns() { for (const auto& e : m) if (CMAX < e.size()) CMAX = e.size(); } void TableFormatter::resizeRows() { for (auto& e : m) if (CMAX > e.size()) e.resize(CMAX); if (CMAX > h.size()) h.resize(CMAX); } void TableFormatter::addRowNumbers() { for (size_t r = 0; r < m.size(); ++r) { m[r][0] = StringConverter::toString(r + 1); } } void TableFormatter::calculateColumnWidths() { std::vector<size_t> n(CMAX, 0); // Инициализация ширинами заголовков for (size_t i = 0; i < h.size() && i < CMAX; ++i) { n[i] = h[i].size(); } // Нахождение максимальных ширин for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] < m[r][c].size()) n[c] = m[r][c].size(); } } // Выравнивание столбцов for (size_t c = 0; c < CMAX; ++c) { for (size_t r = 0; r < m.size(); ++r) { if (n[c] > m[r][c].size()) m[r][c].resize(n[c], ' '); } } // Выравнивание заголовков for (size_t c = 0; c < CMAX; ++c) { if (h[c].size() < m[0][c].size()) h[c].resize(m[0][c].size(), ' '); } } void TableFormatter::alignColumns() {} void TableFormatter::alignHeaders() {} // Реализация TablePrinter TablePrinter::TablePrinter( size_t& cmax, MyVector<MyVector<MyString>>& data, MyVector<MyString>& header) : CMAX(cmax), m(data), h(header) { } void TablePrinter::print(std::wostream& o) { std::wstring line = createLine(); o << line << '\n'; printHeader(o); o << '\n' << line << '\n'; printRows(o); o << line << '\n' << '\n'; } std::wstring TablePrinter::createLine() { std::wstring line; for (size_t c = 0; c < CMAX; ++c) { line += std::wstring(h[c].size() + 3, '='); } return line; } void TablePrinter::printHeader(std::wostream& o) { for (size_t c = 0; c < CMAX; ++c) { o << ' ' << h[c] << " |"; } } void TablePrinter::printRows(std::wostream& o) { for (const auto& r : m) { for (const auto& e : r) { o << ' ' << e << " |"; } o << '\n'; } } // Реализация Tab //Tab::Tab() {}; Tab::Tab() = default; MyVector<MyString>& Tab::operator[](const size_t i) { if (i >= m.size()) m.resize(i + 1); return *(m.begin() + i); } void Tab::formater() { TableFormatter formatter(CMAX, m, h); formatter.format(); } // Реализация оператора вывода std::wostream& operator<<(std::wostream& o, Tab& t) { t.formater(); TablePrinter printer(t.CMAX, t.m, t.h); printer.print(o); return o; } // Реализация TabTest void TabTest::run() { ///-----------------------| /// Создаём табулятор. | ///-----------------------: Tab tab; ///-----------------------| /// Заголовок. | ///-----------------------: tab.head(L"NN", L"Первый", L"Второй", 3); ///-----------------------| /// Как угодно заполняем. | ///-----------------------: tab[1][1] = 2020; tab[2][2] = "qwerty"; tab[4][3] = 3.14; tab[0][4] = L"Яша + Оля"; ///-----------------------| /// Вывод на экран/файл. | ///-----------------------: std::wcout << tab; tab[2][4] = 2025; tab[3][5] = 'A'; std::wcout << tab; }; } Файл Source.cpp: ![]() ![]() #include "DataTable.h"; using namespace school; int main() { setlocale(0, "ru"); TabTest test; test,run(); return 0; } Добавлено Все, нашел ошибку. ![]() ![]() TabTest test; test,run(); Теперь программа компилируется, запускается, но на консоль ничего не выводится,кроме этого сообщения: ![]() ![]() D:\Programming\VS\source\repos\Test\x64\Debug\Test.exe (процесс 30896) завершил работу с кодом -1073741571 (0xc00000fd). Нажмите любую клавишу, чтобы закрыть это окно: Почему? |
![]() |
Сообщ.
#80
,
|
|
Цитата DDim1000 @ 0xc00000fd – переполнение стека, похоже. ![]() ![]() D:\Programming\VS\source\repos\Test\x64\Debug\Test.exe (процесс 30896) завершил работу с кодом -1073741571 (0xc00000fd). Добавлено P.S. Ты на предупреждения не смотришь? ![]() ![]() DataTable.h(36) : warning C4717: school::MyString::operator=<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > >: рекурсия на всех путях выполнения, функция вызовет переполнение стека |
Сообщ.
#81
,
|
|
|
Цитата Qraizer @ P.S. Ты на предупреждения не смотришь? Никаких предупреждений не вижу. Сборка проходит без ошибок и предупреждений. Прикреплённая картинка
Добавлено Все, увидил. 1>D:\Programming\VS\source\repos\Test\Test\DataTable.h(36,1): warning C4717: school::MyString::operator=<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > >: рекурсия на всех путях выполнения, функция вызовет переполнение стека Почему так проиходит? |
Сообщ.
#82
,
|
|
|
Решение:
![]() ![]() // ... // Класс для работы со строками class MyString : public std::wstring { public: MyString(); MyString(std::wstring s); MyString(const wchar_t* s); template<typename T> MyString& operator=(const T a) { std::wstring temp = StringConverter::toString(a); this->assign(temp); // используем базовую реализацию assign из wstring return *this; }; }; // ... |
![]() |
Сообщ.
#83
,
|
|
Цитата DDim1000 @ Потому что template<typename T> MyString& operator=(const T a) принимает любой T, в частности и std::wstring тоже. Поэтому operator=() в строке *this = StringConverter::toString(a) после преобразования любого T в std::wstring снова вызывает сам себя, но уже с T, выведенным как std::wstring, что в свою очередь опять вызовет StringConverter::toString() с аргументом std::wstring в качестве T, т.к. template<typename T> std::wstring StringConverter::toString(T val) опять-таки принимает любой T.Почему так проиходит? Чтобы этого не происходило, нужно отвязать MyString::operator=() от std::wstring, предоставив более специализированную реализацию, или использовать какой-нибудь operator=(), не принадлежащий MyString. Добавлено Я бы написал ![]() ![]() std::wstring temp = StringConverter::toString(a); this->assign(std::move(temp)); // используем базовую реализацию assign из wstring P.S. Если с семантикой перемещения пока не знаком, не бери в голову. Больше путаться будешь, чем выигрывать от оптимизации. Добавлено P.P.S. Ты реализовал второе решение разрыва рекурсии, с использованием методов, не принадлежащих MyString. Альтернативный вариант с более специализированным operator=() может быть таким: просто добавить ещё один operator=(), который перегрузит шаблонный: ![]() ![]() MyString& operator=(const std::wstring& a) { this->std::wstring::operator=(a); /* как вариант: *static_cast<std::wstring*>(this) = a; сработает, т.к. std::wstring является базовым для MyString, и в нём есть свой operator=(std::wstring) */ return *this; } Добавлено Позволь совет на будущее: если программа собирается и даже запускается, это ещё не означает, что с ней всё в порядке. Это должно быть и так понятно, конечно, иначе бы программы после успешной сборки всегда работали идеально. Компилятор не может залезть тебе в голову и понять, что ты хотел написать, поэтому компилит ровно то, что ты написал, а не что хотел написать. Но предупреждения компиляторам выдумали не просто так. Он всё-таки пытается понять написанное в рамках своей скудной разумности, и если увидит что-то непротиворечащее правилам Языка, однако с его точки зрения странное, код-то он соберёт, но предупредит, что тут что-то с его точки зрения не так. Предупреждениям компиляторы не зря научили, к ним всегда стоит присматриваться. Многие этого не понимают и просто их игнорируют, мол, собралось же. Однако даже самое незначительное на первый взгляд, типа "локальная переменная определена и не используется" иногда может указывать на серьёзную ошибку: вместо неё в программе заюзана одноимённая глобальная. Всегда стоит стараться избавляться от предупреждений, причём ни в коем случае не подавлением (кроме ситуаций, когда это зависит не от тебя, и ты просто ничего не можешь сделать; типичный пример – ругательства компилятора на несоответствие знаковости типов внутри какого-нибудь <sys/socket.h>, ибо не ты писал этот идиотский API, а Беркли), а путём исправления кода, иначе за простынёй неважных предупреждений запросто пропустишь реально важные. |
Сообщ.
#84
,
|
|
|
Именно поэтому на всех серьезных проектах ставят warning as error и warning level 4.
|
Сообщ.
#85
,
|
|
|
Цитата sharky72 @ Именно поэтому на всех серьезных проектах ставят warning as error и warning level 4. Это что такое, warning as error и warning level 4? |
![]() |
Сообщ.
#86
,
|
|
Уровни предупреждений от 1 и до... обычно 4, но бывает и больше (у Intel compiler, например, 5) определяют степень важности предупреждения, по какой-то там шкале. Чем больше цифра, тем менее важное. У каждого предупреждения есть некий уровень, и он фиксирован для него. Когда выставляешь желаемый уровень диагностики, предупреждения с большим уровнем компилятор подавляет. Например «warning C4018: <: несоответствие типов со знаком и без знака» у Студии имеет уровень 3, а «warning C4389: ==: несоответствие типов со знаком и без знака» уровень 4, поэтому выставив желаемый уровень 3, увидишь в диагностике первые, но не увидишь вторых.
У разных компиляторов может быть по-разному. Даже распределение предупреждений по уровням, не говоря уже о списке странностей, которые способен обнаружить. По умолчанию уровень обычно 1, когда ставишь 4, компилер будет рассказывать о твоём коде больше, как правило всё, до чего сможет докопаться. Чтобы побить себя по рукам, можно попросить компилятор расценивать любые предупреждения как ошибки, тогда он при обнаружении хотя бы одного, даже самого с его точки зрения некритичного, даже при отсутствии ошибок, будет отказываться собирать код как при ошибках. Довольно кардинально, на спорю. Студия в качестве соломонова решения умеет рассматривать как ошибки только предупреждения с неким указанным уровнем и строже, те, что помягче, не будут стопорить сборку. |
Сообщ.
#87
,
|
|
|
Цитата Qraizer @ Уровни предупреждений от 1 и до... обычно 4, но бывает и больше (у Intel compiler, например, 5) определяют степень важности предупреждения, по какой-то там шкале. Чем больше цифра, тем менее важное. У каждого предупреждения есть некий уровень, и он фиксирован для него. Когда выставляешь желаемый уровень диагностики, предупреждения с большим уровнем компилятор подавляет. Например «warning C4018: <: несоответствие типов со знаком и без знака» у Студии имеет уровень 3, а «warning C4389: ==: несоответствие типов со знаком и без знака» уровень 4, поэтому выставив желаемый уровень 3, увидишь в диагностике первые, но не увидишь вторых. У разных компиляторов может быть по-разному. Даже распределение предупреждений по уровням, не говоря уже о списке странностей, которые способен обнаружить. По умолчанию уровень обычно 1, когда ставишь 4, компилер будет рассказывать о твоём коде больше, как правило всё, до чего сможет докопаться. Чтобы побить себя по рукам, можно попросить компилятор расценивать любые предупреждения как ошибки, тогда он при обнаружении хотя бы одного, даже самого с его точки зрения некритичного, даже при отсутствии ошибок, будет отказываться собирать код как при ошибках. Довольно кардинально, на спорю. Студия в качестве соломонова решения умеет рассматривать как ошибки только предупреждения с неким указанным уровнем и строже, те, что помягче, не будут стопорить сборку. Ой, Qraizer, ну ты же знаешь как я тебя уважаю! Но ты часто бываешь такой "душный" =) Ну чтобы просто сказать, мол "увеличение уровня предупреждений увеличивает детализацию сообщений об ошибках". Это всё. Обнять и плакать ![]() |
![]() |
Сообщ.
#88
,
|
|
У меня пунктик: не просто выдать факт, а объяснить его, идеально – почему так, а не как-нибудь иначе. Связи концепций лучше усваиваются и дают понимание системы, а не просто её знание.
|
Сообщ.
#89
,
|
|
|
![]() ![]() |