Przejdź do głównej zawartości

Zmienne » Napisy » Potencjalne błędy

Potencjalne błędy

Nie dołączenie odpowiedniego nagłówka

❌ Niepoprawny kod
int main()
{
std::string s = "Hello";
}

🔴 Treść błędu:

In function 'int main()':
error: 'string' is not a member of 'std'
2 | std::string s = "Hello";
| ^~~~~~
note: 'std::string' is defined in header '<string>'; did you forget to '#include <string>'?
+++ |+#include <string>
1 | int main() {

👌 Rozwiązanie problemu

Dodaj linijkę #include <string> na górze swojego pliku.

Traktowanie liczb jako napisów

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = 123;
}

🔴 Treść błędu:

In function 'int main()':
error: conversion from 'int' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string<char>'} requested
4 | std::string s = 123;
| ^~~

👌 Rozwiązanie problemu

Użyj funkcji std::to_string, aby przekonwertować liczbę na napis. Zobacz Konwersja pomiędzy liczbami i tekstem i Liczby i napisy po więcej informacji.

Traktowanie napisów jako liczb

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = "123";
s *= 15;
}

🔴 Treść błędu:

In function 'int main()':
error: no match for 'operator*=' (operand types are 'std::string' {aka 'std::__cxx11::basic_string<char>'} and 'int')
5 | s *= 15;
| ~~^~~~~

👌 Rozwiązanie problemu

Użyj funkcji std::stoi lub std::stof, aby przekonwertować łańcuch znaków na liczbę. Zobacz Konwersja pomiędzy liczbami i tekstem i Liczby i napisy po więcej informacji.

Łączenie dwóch literałów stringowych

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = "Hello " + "World";
}

🔴 Treść błędu:

In function 'int main()':
error: invalid operands of types 'const char [7]' and 'const char [6]' to binary 'operator+'
5 | std::string s = "Hello " + "World";
| ~~~~~~~~ ^ ~~~~~~~
| | |
| | const char [6]
| const char [7]

👌 Rozwiązanie problemu

Zawsze kiedy widzisz w kodzie C++ "TEKST", mówimy na to literał stringowy (ang.: string literal). Jest to śmieszny termin na wartość wpisaną na sztywno (ang.: hardcoded). Ważne jest jednak to, że literał stringowy to nie to samo co std::string! Mają inne typy i trochę inne właściwości. Typ literału stringowego to const char[N], gdzie N to długość tego tekstu + 1. Błąd z kompilatora GCC wyżej pokazuje to najlepiej.

const char[N] to tablicą. Tablice omówimy w późniejszym rozdziale, ale ważne jest to, że nie można do siebie dodać dwóch tablic, ponieważ nie są one liczbami. std::string w specjalny sposób zapewnia nam taką możliwość (co również omówimy dalej w kursie).

Najprostszym sposobem na naprawienie tego błędu jest połączenie tych literałów własnoręcznie: "Hello World".

Ominięcie wywołania std::getline

⚠️ Buggy Code
#include <string>

int main()
{
int age;
std::string full_name;

std::cout << "Enter your age: ";
std::cin >> age;

std::cout << "Enter your full name: ";
std::getline(std::cin, full_name); // Ta linia wydaje się nie wykonywać

std::cout << "\nYour name is " << full_name << " and your age is " << age;
}

Niespodziewane Zachowanie

Szybko zauważysz, że używająć std::getline, zaraz po użyciu std::cin >> ... w jakiejkolwiek formie, wywołanie std::getline wydaje się być ominięte. W przykładzie wyżej, program zapyta użytkownika o wiek, użytkownik wprowadziłby, powiedzmy 23, a następnie program zapytałby o imię, lecz użytkownik nie miałby szansy nic wpisać. Zamiast tego, program od razu wypisałby Your name is and your age is 23.

👌 Rozwiązanie problemu

Zrozumienie dlaczego tak się dzieje jest troszkę skomplikowane. Na początek przypomnijmy, że std::cin wyciąga dane z stdin aż do napotkania białych znaków (spacja, nowa linia lub tabulator). Kiedy użytkownik naciśnie Enter, aby wprowadzić wiek do programu, znak nowej linii \n zostaje wprowadzony do stdin. std::cin sczytuje dane, które użytkownik wpisał i zatrzymuje się na nowej linii nie ignorując jej.

std::getline określa koniec tekstu na podstawie znaków nowej lini w tekście. Więc kiedy aby funkcja ta próbuje pobrać od nas kolejne dane z *stdin aż do napotkania \n, od razu widzi ona ten znak, który został tam zostawiony przez poprzednie wywołanie std::cin >> age;. Interpretuje więc ona to jako koniec danych i ustawia full_name jako pusty napis "".

Rozwiązaniem tego problemu jest użyciem kolejnej metody - ignore. Ta metoda mówi std::cin, aby ominął on pozostawiony z poprzedniej operacji znak nowej linii.

Za każdym razem, kiedy chcesz użyć std::getline po użyciu std::cin, powinieneś wywołać std::cin.ignore() pomiędzy tymi operacjami, jak w tym przykładzie:

Jak użyć metody ignore
int age;
std::string full_name;

std::cin >> age;

std::cin.ignore();

std::getline(std::cin, full_name);

Zmienne » Napisy » Potencjalne błędy

Potencjalne błędy

Nie dołączenie odpowiedniego nagłówka

❌ Niepoprawny kod
int main()
{
std::string s = "Hello";
}

🔴 Treść błędu:

In function 'int main()':
error: 'string' is not a member of 'std'
2 | std::string s = "Hello";
| ^~~~~~
note: 'std::string' is defined in header '<string>'; did you forget to '#include <string>'?
+++ |+#include <string>
1 | int main() {

👌 Rozwiązanie problemu

Dodaj linijkę #include <string> na górze swojego pliku.

Traktowanie liczb jako napisów

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = 123;
}

🔴 Treść błędu:

In function 'int main()':
error: conversion from 'int' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string<char>'} requested
4 | std::string s = 123;
| ^~~

👌 Rozwiązanie problemu

Użyj funkcji std::to_string, aby przekonwertować liczbę na napis. Zobacz Konwersja pomiędzy liczbami i tekstem i Liczby i napisy po więcej informacji.

Traktowanie napisów jako liczb

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = "123";
s *= 15;
}

🔴 Treść błędu:

In function 'int main()':
error: no match for 'operator*=' (operand types are 'std::string' {aka 'std::__cxx11::basic_string<char>'} and 'int')
5 | s *= 15;
| ~~^~~~~

👌 Rozwiązanie problemu

Użyj funkcji std::stoi lub std::stof, aby przekonwertować łańcuch znaków na liczbę. Zobacz Konwersja pomiędzy liczbami i tekstem i Liczby i napisy po więcej informacji.

Łączenie dwóch literałów stringowych

❌ Niepoprawny kod
#include <string>

int main()
{
std::string s = "Hello " + "World";
}

🔴 Treść błędu:

In function 'int main()':
error: invalid operands of types 'const char [7]' and 'const char [6]' to binary 'operator+'
5 | std::string s = "Hello " + "World";
| ~~~~~~~~ ^ ~~~~~~~
| | |
| | const char [6]
| const char [7]

👌 Rozwiązanie problemu

Zawsze kiedy widzisz w kodzie C++ "TEKST", mówimy na to literał stringowy (ang.: string literal). Jest to śmieszny termin na wartość wpisaną na sztywno (ang.: hardcoded). Ważne jest jednak to, że literał stringowy to nie to samo co std::string! Mają inne typy i trochę inne właściwości. Typ literału stringowego to const char[N], gdzie N to długość tego tekstu + 1. Błąd z kompilatora GCC wyżej pokazuje to najlepiej.

const char[N] to tablicą. Tablice omówimy w późniejszym rozdziale, ale ważne jest to, że nie można do siebie dodać dwóch tablic, ponieważ nie są one liczbami. std::string w specjalny sposób zapewnia nam taką możliwość (co również omówimy dalej w kursie).

Najprostszym sposobem na naprawienie tego błędu jest połączenie tych literałów własnoręcznie: "Hello World".

Ominięcie wywołania std::getline

⚠️ Buggy Code
#include <string>

int main()
{
int age;
std::string full_name;

std::cout << "Enter your age: ";
std::cin >> age;

std::cout << "Enter your full name: ";
std::getline(std::cin, full_name); // Ta linia wydaje się nie wykonywać

std::cout << "\nYour name is " << full_name << " and your age is " << age;
}

Niespodziewane Zachowanie

Szybko zauważysz, że używająć std::getline, zaraz po użyciu std::cin >> ... w jakiejkolwiek formie, wywołanie std::getline wydaje się być ominięte. W przykładzie wyżej, program zapyta użytkownika o wiek, użytkownik wprowadziłby, powiedzmy 23, a następnie program zapytałby o imię, lecz użytkownik nie miałby szansy nic wpisać. Zamiast tego, program od razu wypisałby Your name is and your age is 23.

👌 Rozwiązanie problemu

Zrozumienie dlaczego tak się dzieje jest troszkę skomplikowane. Na początek przypomnijmy, że std::cin wyciąga dane z stdin aż do napotkania białych znaków (spacja, nowa linia lub tabulator). Kiedy użytkownik naciśnie Enter, aby wprowadzić wiek do programu, znak nowej linii \n zostaje wprowadzony do stdin. std::cin sczytuje dane, które użytkownik wpisał i zatrzymuje się na nowej linii nie ignorując jej.

std::getline określa koniec tekstu na podstawie znaków nowej lini w tekście. Więc kiedy aby funkcja ta próbuje pobrać od nas kolejne dane z *stdin aż do napotkania \n, od razu widzi ona ten znak, który został tam zostawiony przez poprzednie wywołanie std::cin >> age;. Interpretuje więc ona to jako koniec danych i ustawia full_name jako pusty napis "".

Rozwiązaniem tego problemu jest użyciem kolejnej metody - ignore. Ta metoda mówi std::cin, aby ominął on pozostawiony z poprzedniej operacji znak nowej linii.

Za każdym razem, kiedy chcesz użyć std::getline po użyciu std::cin, powinieneś wywołać std::cin.ignore() pomiędzy tymi operacjami, jak w tym przykładzie:

Jak użyć metody ignore
int age;
std::string full_name;

std::cin >> age;

std::cin.ignore();

std::getline(std::cin, full_name);