Here are the instructions:
In Chapter 10, the class clockType
was designed to implement the time of day in a program. Certain applications, in addition to hours, minutes, and seconds, might require you to store the time zone.
Derive the class extClockType
from the class clockType
by adding a member variable to store the time zone. Add the necessary member functions and constructors to make the class functional. Also, write the definitions of the member functions and the constructors. Finally, write a test program to test your class.
Here are my files:
clockType.h
//clockType.h, the specification file for the class clockType
#ifndef H_ClockType
#define H_ClockType
class clockType
{
public:
void setTime(int hours, int minutes, int seconds);
//Function to set the time.
//The time is set according to the parameters.
//Postcondition: hr = hours; min = minutes;
// sec = seconds
// The function checks whether the values of
// hours, minutes, and seconds are valid. If a
// value is invalid, the default value 0 is
// assigned.
void getTime(int& hours, int& minutes, int& seconds) const;
//Function to return the time.
//Postcondition: hours = hr; minutes = min;
// seconds = sec
void printTime() const;
//Function to print the time.
//Postcondition: The time is printed in the form
// hh:mm:ss.
void incrementSeconds();
//Function to increment the time by one second.
//Postcondition: The time is incremented by one
// second.
// If the before-increment time is 23:59:59, the
// time is reset to 00:00:00.
void incrementMinutes();
//Function to increment the time by one minute.
//Postcondition: The time is incremented by one
// minute.
// If the before-increment time is 23:59:53,
// the time is reset to 00:00:53.
void incrementHours();
//Function to increment the time by one hour.
//Postcondition: The time is incremented by one
// hour.
// If the before-increment time is 23:45:53, the
// time is reset to 00:45:53.
bool equalTime(const clockType& otherClock) const;
//Function to compare the two times.
//Postcondition: Returns true if this time is
// equal to otherClock; otherwise,
// returns false.
clockType(int hours, int minutes, int seconds);
//constructor with parameters
//The time is set according to the parameters.
//Postcondition: hr = hours; min = minutes;
// sec = seconds
// The constructor checks whether the values of
// hours, minutes, and seconds are valid. If a
// value is invalid, the default value 0 is
// assigned.
clockType();
//default constructor with parameters
//The time is set to 00:00:00.
//Postcondition: hr = 0; min = 0; sec = 0
private:
int hr; //variable to store the hours
int min; //variable to store the minutes
int sec; //variable to store the seconds
};
#endif
clockTypeImp.cpp
//Implementation File for the class clockType
#include <iostream>
#include "clockType.h"
using namespace std;
void clockType::setTime(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
}
void clockType::getTime(int& hours, int& minutes,
int& seconds) const
{
hours = hr;
minutes = min;
seconds = sec;
}
void clockType::incrementHours()
{
hr++;
if (hr > 23)
hr = 0;
}
void clockType::incrementMinutes()
{
min++;
if (min > 59)
{
min = 0;
incrementHours();
}
}
void clockType::incrementSeconds()
{
sec++;
if (sec > 59)
{
sec = 0;
incrementMinutes();
}
}
void clockType::printTime() const
{
if (hr < 10)
cout << "0";
cout << hr << ":";
if (min < 10)
cout << "0";
cout << min << ":";
if (sec < 10)
cout << "0";
cout << sec;
}
bool clockType::equalTime(const clockType& otherClock) const
{
return (hr == otherClock.hr
&& min == otherClock.min
&& sec == otherClock.sec);
}
clockType::clockType(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
}
clockType::clockType() //default constructor
{
hr = 0;
min = 0;
sec = 0;
}
(these first 2 files are given)
(starting here is where i create the code)
extClockType.h
#ifndef H_extClockType
#define H_extClockType
#include<iostream>
#include "clockType.h"
using namespace std;
class extClockType: public clockType
{
public:
extClockType();
extClockType(int, int, int, string);
void setTime(int hours, int minutes, int seconds, string zone);
string printTimezone();
string getTimezone();
private:
string zone;
};
#endif
extClockTypeImp.cpp
#include <iostream>
#include "clockType.h"
#include "extClockType.h"
using namespace std;
extClockType::extClockType(): clockType()
{
zone = "na";
}
extClockType::extClockType(int hours, int minutes, int seconds, string time_zone)
{
clockType::setTime(hours, minutes, seconds);
zone = time_zone;
}
void extClockType::setTime(int hours, int minutes, int seconds, string zone)
{
clockType::setTime(hours, minutes, seconds);
}
string extClockType::getTimezone()
{
return zone;
}
string extClockType::printTimezone()
{
clockType::printTime();
cout << " " << zone << endl;
return 0;
}
main.cpp
#include <string>
#include <iomanip>
#include <iostream>
#include "clockType.h"
#include "extClockType.h"
using namespace std;
int main()
{
extClockType time1(5, 10, 34, "CST");
cout << "Time 1: ";
time1.printTimezone();
cout<<endl;
extClockType time2;
time2.setTime(12, 45, 59, "PST");
cout<< "Time 2: ";
time2.printTimezone();
cout<<endl;
time2.incrementSeconds();
cout<< "After incrementing time 2 by one second, Time 2:";
time2.printTime();
}
This is my error message:
Time 1: 05:10:34 CST
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Sorry for the post being long I just wanted to show all of the files I’m working with. If anyone is willing to try to help me that would be a huge help!
My program crashes!
So this is my program, what I want to do is create a character from a game with certain stats, strength, armor, critical chance, etc.
When I executed the program instantly crashes and says:
terminate called after throwing an instance of ‘std::logic_error’
what(): basic_string::_S_construct null not valid
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application’s support team for more information.
Process returned 3 (0x3)
|
|
Last edited on
Line 22: C++ is not Java. Strings cannot be null.
Damm it… k thanks! I’ll just put a default name
One more question, how can I separate this program now into different .h and .cpp files? I’ve never done it :/
I’ve read it already, but it didn’t work, could you separate this program in different files to see how to do it? If you don’t have time tell me and I’ll create a new post.
Thanks for your help anyway
Line 65 66
|
|
I have NEVER understood how this pointer works… I mean when do I use it? what does it do exactly?
Maybe now that you have brought this pointer out you could help me
Lines 12-23 go in header file (player.h).
Lines 25-67 go in a .cpp file (player.cpp)
Lines 69-94 go in main.cpp (or whatever you want to call your program)
You’ll need to repeat lines 1-8 in both your .cpp files and add an #include of your header file.
You also need to get rid of the global at line 10. It belongs as a local inside main().
Line 47: You don’t want your call to srand() inside set_random_stats(). That will reinitialize the RNG each time you call set_random_stats(). Move it to line 70.
Line 65-66: You don’t need the new_player:: scope qualifiers here, although they’re harmless.
THANKS!!!! How did you guys learn so much of coding? I have read jumping into c++ and still struggle quite a lot :/
Thanks for all!!
@andy1192 — Yes new_player is indeed known. There’s just no need for it here. Using the this->
pointer is not needed either since create_character() is a member of the new_player class.
@erik341 — The this
pointer simply points to the current instance of the object. Not usually needed, but can be useful if you can be helpful if you have naming conflicts between arguments and members.
Consider if set_name() took an argument called name_player:
|
|
this
pointer can also be useful if you want to pass a pointer to the current object to a function that’s not a member of the class.
Last edited on
My compiler (VS Express 2012) says for line 22:
Error C2864: ‘new_player::name_player’: only static const integral data members can be initiated within a class.
and IntelliSence reports:
data member initialization is not allowed
This seems to agree with your error message:
basic_string::_S_construct null not valid
Style issue:
If you have using namespace std;
in line 8, you don’ t need the std:: prefix in lines 21 and 22. However, if you leave the namespace out of line 8, you’ll need to add std:: before string, because string is in the std namespace. In other words, this:
|
|
will work
Leave out the using statement:
|
|
is an error because string is in namespace std too.
|
|
Last edited on
Wow! You explained it better than any webpage I’ve read (AbstractionAnon)! Thank you very much! Can I private message you if I need help further on?
Last edited on
Topic archived. No new replies allowed.
Вот инструкции:
В главе 10 класс clockType
был разработан для реализации времени суток в программе. Для некоторых приложений, помимо часов, минут и секунд, может потребоваться сохранение часового пояса.
Выведите класс extClockType
из класса clockType
добавив переменную-член для хранения часового пояса. Добавьте необходимые функции-члены и конструкторы, чтобы сделать класс функциональным. Кроме того, напишите определения функций-членов и конструкторов. Наконец, напишите тестовую программу для проверки вашего класса.
Вот мои файлы:
clockType.h
//clockType.h, the specification file for the class clockType
#ifndef H_ClockType
#define H_ClockType
class clockType
{
public:
void setTime(int hours, int minutes, int seconds);
//Function to set the time.
//The time is set according to the parameters.
//Postcondition: hr = hours; min = minutes;
// sec = seconds
// The function checks whether the values of
// hours, minutes, and seconds are valid. If a
// value is invalid, the default value 0 is
// assigned.
void getTime(int& hours, int& minutes, int& seconds) const;
//Function to return the time.
//Postcondition: hours = hr; minutes = min;
// seconds = sec
void printTime() const;
//Function to print the time.
//Postcondition: The time is printed in the form
// hh:mm:ss.
void incrementSeconds();
//Function to increment the time by one second.
//Postcondition: The time is incremented by one
// second.
// If the before-increment time is 23:59:59, the
// time is reset to 00:00:00.
void incrementMinutes();
//Function to increment the time by one minute.
//Postcondition: The time is incremented by one
// minute.
// If the before-increment time is 23:59:53,
// the time is reset to 00:00:53.
void incrementHours();
//Function to increment the time by one hour.
//Postcondition: The time is incremented by one
// hour.
// If the before-increment time is 23:45:53, the
// time is reset to 00:45:53.
bool equalTime(const clockType& otherClock) const;
//Function to compare the two times.
//Postcondition: Returns true if this time is
// equal to otherClock; otherwise,
// returns false.
clockType(int hours, int minutes, int seconds);
//constructor with parameters
//The time is set according to the parameters.
//Postcondition: hr = hours; min = minutes;
// sec = seconds
// The constructor checks whether the values of
// hours, minutes, and seconds are valid. If a
// value is invalid, the default value 0 is
// assigned.
clockType();
//default constructor with parameters
//The time is set to 00:00:00.
//Postcondition: hr = 0; min = 0; sec = 0
private:
int hr; //variable to store the hours
int min; //variable to store the minutes
int sec; //variable to store the seconds
};
#endif
clockTypeImp.cpp
//Implementation File for the class clockType
#include <iostream>
#include "clockType.h"
using namespace std;
void clockType::setTime(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
}
void clockType::getTime(int& hours, int& minutes,
int& seconds) const
{
hours = hr;
minutes = min;
seconds = sec;
}
void clockType::incrementHours()
{
hr++;
if (hr > 23)
hr = 0;
}
void clockType::incrementMinutes()
{
min++;
if (min > 59)
{
min = 0;
incrementHours();
}
}
void clockType::incrementSeconds()
{
sec++;
if (sec > 59)
{
sec = 0;
incrementMinutes();
}
}
void clockType::printTime() const
{
if (hr < 10)
cout << "0";
cout << hr << ":";
if (min < 10)
cout << "0";
cout << min << ":";
if (sec < 10)
cout << "0";
cout << sec;
}
bool clockType::equalTime(const clockType& otherClock) const
{
return (hr == otherClock.hr
&& min == otherClock.min
&& sec == otherClock.sec);
}
clockType::clockType(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
}
clockType::clockType() //default constructor
{
hr = 0;
min = 0;
sec = 0;
}
(даны первые 2 файла)
(начиная здесь я создаю код)
extClockType.h
#ifndef H_extClockType
#define H_extClockType
#include<iostream>
#include "clockType.h"
using namespace std;
class extClockType: public clockType
{
public:
extClockType();
extClockType(int, int, int, string);
void setTime(int hours, int minutes, int seconds, string zone);
string printTimezone();
string getTimezone();
private:
string zone;
};
#endif
extClockTypeImp.cpp
#include <iostream>
#include "clockType.h"
#include "extClockType.h"
using namespace std;
extClockType::extClockType(): clockType()
{
zone = "na";
}
extClockType::extClockType(int hours, int minutes, int seconds, string time_zone)
{
clockType::setTime(hours, minutes, seconds);
zone = time_zone;
}
void extClockType::setTime(int hours, int minutes, int seconds, string zone)
{
clockType::setTime(hours, minutes, seconds);
}
string extClockType::getTimezone()
{
return zone;
}
string extClockType::printTimezone()
{
clockType::printTime();
cout << " " << zone << endl;
return 0;
}
main.cpp
#include <string>
#include <iomanip>
#include <iostream>
#include "clockType.h"
#include "extClockType.h"
using namespace std;
int main()
{
extClockType time1(5, 10, 34, "CST");
cout << "Time 1: ";
time1.printTimezone();
cout<<endl;
extClockType time2;
time2.setTime(12, 45, 59, "PST");
cout<< "Time 2: ";
time2.printTimezone();
cout<<endl;
time2.incrementSeconds();
cout<< "After incrementing time 2 by one second, Time 2:";
time2.printTime();
}
Это мое сообщение об ошибке:
Time 1: 05:10:34 CST
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Извините за длинное сообщение, я просто хотел показать все файлы, с которыми я работаю. Если кто-нибудь захочет помочь мне, это будет огромной помощью!
2
Решение
Ошибка просто означает, что вы вызвали строковый конструктор:
std::string::string(const char *p);
с NULL
указатель, который недопустим.
Используйте отладчик, чтобы узнать именно так где ваш код сделал это, и исправить это, чтобы не делать этого.
3
Другие решения
Одна ошибка заключается в следующем:
string extClockType::printTimezone()
{
clockType::printTime();
cout << " " << zone << endl;
return 0; // This is not legal, converting an int to a std::string
}
По возвращении вы возвращаетесь 0
и это пытаются преобразовать в std::string
, Нет конструктора для std::string
это занимает int
аргумент (в этом случае 0
). Вместо этого будет выдано исключение.
Вы должны вернуть либо ничего (таким образом, функция должна быть объявлена как возвращающая void
) или вернуть что-то, что создает действительный std::string
,
Примечание: конструктор, который был вызван для std::string
это тот, который занимает один const char *
аргумент (см. конструктор (5)). В этом случае, 0
преобразуется в nullptr
Таким образом, по возвращении std::string(nullptr)
пытается быть построен.
1
Легко ведь
string extClockType::printTimezone() {
...
return 0;
}
Измените это на это и обновите прототип:
void extClockType::printTimezone() {
...
return;
}
Это пытается создать std :: string с 0, интерпретируемым как nullptr. Возвращаемое значение должно быть недействительным в зависимости от использования.
После исправления вывод будет:
Время 1: 05:10:34 CST
Время 2: 12:45:59 на
После увеличения времени 2 на одну секунду, Время 2: 12: 46: 00
Обратите внимание, что «на» является результатом
void setTime(int hours, int minutes, int seconds, string zone);
фактически не устанавливает переменную-член ‘zone’.
0
Содержание
- terminate called after throwing instance of std::invalid_argument C++
- Introduction
- Protocols for valid input string
- Exceptions in stoi, stol, stoll
- std::invalid_argument
- std::out_of_range
- Solution
- std::stoi, std::stol, std::stoll
- What is Stoll CPP?
- What is stoi CPP?
- What is the difference between atoi and stoi?
- What library is stoi in C++?
- Does stoi ignore whitespace?
- Where is stoi defined?
- Does stoi throw exception?
- Parameters
- Return value
- std:: stoi, std:: stol, std:: stoll
- Contents
- [edit] Parameters
- [edit] Return value
- std:: stoi, std:: stol, std:: stoll
- Contents
- [edit] Parameters
- [edit] Return value
- Исключения в C++: типы, синтаксис и обработка
- Инструмент программирования для исключительных ситуаций
- Исключения: панацея или нет
- Синтаксис исключений в C++
- Ловим исключения
- Ловим исключения нескольких типов
- Ловим все исключения
- Перебрасываем исключение
- Принимаем исключение по ссылке
- Выбрасываем исключения
- Создаём типы для исключений
- Ловим исключение по базовому типу
- Выбрасываем исключение в тернарной операции ?:
- Вся функция — try-блок
- Исключения в конструкторе
- Исключения в деструкторе
- Обрабатываем непойманные исключения
- Базовые исключения стандартной библиотеки
- std::exception
- std::logic_error : public std::exception
- std::runtime_error : public std::exception
- std::bad_alloc : public std::exception
- Заключение
terminate called after throwing instance of std::invalid_argument C++
Table of Contents Hide
In this article we will resolve the c++ issue related to “terminate called after throwing an instance of ‘std::invalid_argument’ what(): stoi“.
Introduction
Suppose you have a string and you want to interpret the signed integer from it using c++, then you may use any of these functions –
- std::stoi() – Used for int return type.
- std::stol() – Used for long return type.
- std::stoll() – Used for long long return type.
Their method signatures are as follows –
Protocols for valid input string
These methods can convert an input string to unsigned number. But for that the string needs to follow some protocols like –
- It should start with either blank spaces, +/-, numbers or alphabets depending on base value.
- Range of the valid base values are – 0 and 2 to 36.
- If it starts with 0 then base needs to be 0 or 8.
- If it starts with 0x or 0X then base needs to be 0 or 16.
- Valid numbers and alphabets in string according to base values are –
Base Value | Valid Range |
---|---|
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 |
Exceptions in stoi, stol, stoll
There are two kinds of exceptions –
std::invalid_argument
Any string that is not following the above protocol will raise std::invalid_argument exception. So, few of the examples are –
Error because the string starts with alphabets but the base is 10. In the table above we indicated the valid values for all the base values. In stoi, stol, stoll functions we have base 10 by default.
Error because the string starts with f which indicates the hexadecimal number but we provided the base as 10.
Here the output will be 2629 as the decimal value of A45 with base 16. Here G6 is truncated because G is not a valid base 16 character. Hence the stoi function took every valid digits before G .
std::out_of_range
You will get this error when the input value is bigger than what function can support. If you provide a numeric string to stoi which returns int value then the string needs to have number smaller than max-int (2147483647).
Solution
The solution to this problem is to follow the input string protocol as described above. Otherwise you will get either of the two exceptions.
Источник
std::stoi, std::stol, std::stoll
Defined in header | |
---|---|
(1) | (since C++11) |
(2) | (since C++11) |
(3) | (since C++11) |
Interprets a signed integer value in the string str .
What is Stoll CPP?
What is stoi CPP?
What is the difference between atoi and stoi?
What library is stoi in C++?
Does stoi ignore whitespace?
Where is stoi defined?
Does stoi throw exception?
Discards any whitespace characters (as identified by calling std::isspace ) until the first non-whitespace character is found, then takes as many characters as possible to form a validbase-n(where n= base ) integer number representation and converts them to an integer value. The valid integer value consists of the following parts:
- (optional) plus or minus sign
- (optional) prefix ( 0 ) indicating octal base (applies only when the base is 8 or 0 )
- (optional) prefix ( 0x or 0X ) indicating hexadecimal base (applies only when the base is 16 or 0 )
- a sequence of digits
The set of valid values for base is <0,2,3. 36>. The set of valid digits for base- 2 integers is <0,1>, for base- 3 integers is <0,1,2>, and so on. For bases larger than 10 , valid digits include alphabetic characters, starting from Aa for base- 11 integer, to Zz for base- 36 integer. The case of the characters is ignored.
Additional numeric formats may be accepted by the currently installed C locale.
If the value of base is 0 , the numeric base is auto-detected: if the prefix is 0 , the base is octal, if the prefix is 0x or 0X , the base is hexadecimal, otherwise the base is decimal.
If the minus sign was part of the input sequence, the numeric value calculated from the sequence of digits is negated as if by unary minus in the result type.
If pos is not a null pointer, then a pointer ptr — internal to the conversion functions — will receive the address of the first unconverted character in str.c_str() , and the index of that character will be calculated and stored in *pos , giving the number of characters that were processed by the conversion.
Parameters
str | — | the string to convert |
pos | — | address of an integer to store the number of characters processed |
base | — | the number base |
Return value
Integer value corresponding to the content of str.
Источник
std:: stoi, std:: stol, std:: stoll
Compiler support |
Freestanding and hosted |
Language |
Standard library |
Standard library headers |
Named requirements |
Feature test macros (C++20) |
Language support library |
Concepts library (C++20) |
Metaprogramming library (C++11) |
Diagnostics library |
General utilities library |
Strings library |
Containers library |
Iterators library |
Ranges library (C++20) |
Algorithms library |
Numerics library |
Localizations library |
Input/output library |
Filesystem library (C++17) |
Regular expressions library (C++11) |
Concurrency support library (C++11) |
Technical specifications |
Symbols index |
External libraries |
Interprets a signed integer value in the string str .
Discards any whitespace characters (as identified by calling std::isspace) until the first non-whitespace character is found, then takes as many characters as possible to form a valid base-n (where n= base ) integer number representation and converts them to an integer value. The valid integer value consists of the following parts:
- (optional) plus or minus sign
- (optional) prefix ( ) indicating octal base (applies only when the base is 8 or 0 )
- (optional) prefix ( 0x or 0X ) indicating hexadecimal base (applies only when the base is 16 or 0 )
- a sequence of digits
The set of valid values for base is <0,2,3. 36>. The set of valid digits for base- 2 integers is <0,1>, for base- 3 integers is <0,1,2>, and so on. For bases larger than 10 , valid digits include alphabetic characters, starting from Aa for base- 11 integer, to Zz for base- 36 integer. The case of the characters is ignored.
Additional numeric formats may be accepted by the currently installed C locale.
If the value of base is 0 , the numeric base is auto-detected: if the prefix is , the base is octal, if the prefix is 0x or 0X , the base is hexadecimal, otherwise the base is decimal.
If the minus sign was part of the input sequence, the numeric value calculated from the sequence of digits is negated as if by unary minus in the result type.
If pos is not a null pointer, then a pointer ptr — internal to the conversion functions — will receive the address of the first unconverted character in str. c_str ( ) , and the index of that character will be calculated and stored in * pos , giving the number of characters that were processed by the conversion.
Contents
[edit] Parameters
Null-terminated strings |
Byte strings |
Multibyte strings |
Wide strings |
Classes |
str | — | the string to convert |
pos | — | address of an integer to store the number of characters processed |
base | — | the number base |
[edit] Return value
Integer value corresponding to the content of str.
Источник
std:: stoi, std:: stol, std:: stoll
Compiler support |
Freestanding and hosted |
Language |
Standard library |
Standard library headers |
Named requirements |
Feature test macros (C++20) |
Language support library |
Concepts library (C++20) |
Metaprogramming library (C++11) |
Diagnostics library |
General utilities library |
Strings library |
Containers library |
Iterators library |
Ranges library (C++20) |
Algorithms library |
Numerics library |
Localizations library |
Input/output library |
Filesystem library (C++17) |
Regular expressions library (C++11) |
Concurrency support library (C++11) |
Technical specifications |
Symbols index |
External libraries |
Interprets a signed integer value in the string str .
Discards any whitespace characters (as identified by calling std::isspace) until the first non-whitespace character is found, then takes as many characters as possible to form a valid base-n (where n= base ) integer number representation and converts them to an integer value. The valid integer value consists of the following parts:
- (optional) plus or minus sign
- (optional) prefix ( ) indicating octal base (applies only when the base is 8 or 0 )
- (optional) prefix ( 0x or 0X ) indicating hexadecimal base (applies only when the base is 16 or 0 )
- a sequence of digits
The set of valid values for base is <0,2,3. 36>. The set of valid digits for base- 2 integers is <0,1>, for base- 3 integers is <0,1,2>, and so on. For bases larger than 10 , valid digits include alphabetic characters, starting from Aa for base- 11 integer, to Zz for base- 36 integer. The case of the characters is ignored.
Additional numeric formats may be accepted by the currently installed C locale.
If the value of base is 0 , the numeric base is auto-detected: if the prefix is , the base is octal, if the prefix is 0x or 0X , the base is hexadecimal, otherwise the base is decimal.
If the minus sign was part of the input sequence, the numeric value calculated from the sequence of digits is negated as if by unary minus in the result type.
If pos is not a null pointer, then a pointer ptr — internal to the conversion functions — will receive the address of the first unconverted character in str. c_str ( ) , and the index of that character will be calculated and stored in * pos , giving the number of characters that were processed by the conversion.
Contents
[edit] Parameters
Null-terminated strings |
Byte strings |
Multibyte strings |
Wide strings |
Classes |
str | — | the string to convert |
pos | — | address of an integer to store the number of characters processed |
base | — | the number base |
[edit] Return value
Integer value corresponding to the content of str.
Источник
Исключения в C++: типы, синтаксис и обработка
Поговорим об исключениях в C++, начиная определением и заканчивая грамотной обработкой.
В статье я постарался раскрыть тему исключений достаточно подробно. Она будет полезна новичкам, чтобы узнать об исключениях, и программистам с опытом, чтобы углубиться в явление и достичь его полного понимания.
Статья поделена на две части. Первая перед вами и содержит базовые, но важные сведения. Вторая выйдет чуть позже. В ней — информация для более продвинутых разработчиков.
В первой части разберёмся:
- для чего нужны исключения;
- особенности C++;
- синтаксис выбрасывания и обработки исключений;
- особые случаи, связанные с исключениями.
Также рассмотрим основные стандартные типы исключений, где и для чего они применяются.
Мы опираемся на современные компиляторы и Стандарт C++20. Немного затронем C++23 и даже C++03.
Если вы только осваиваете C++, возможно, вам будет интересен курс «Разработчик C++» в Яндекс Практикуме. У курса есть бесплатная вводная часть. Именно она может стать вашим первым шагом в мир C++. Для тех, кто знаком с программированием, есть внушительная ознакомительная часть, тоже бесплатная.
Инструмент программирования для исключительных ситуаций
В жизни любой программы бывают моменты, когда всё идёт не совсем так, как задумывал разработчик. Например:
- в системе закончилась оперативная память;
- соединение с сервером внезапно прервалось;
- пользователь выдернул флешку во время чтения или записи файла;
- понадобилось получить первый элемент списка, который оказался пустым;
- формат файла не такой, как ожидалось.
Примеры объединяет одно: возникшая ситуация достаточно редка, и при нормальной работе программы, всех устройств, сети и адекватном поведении пользователя она не возникает.
Хороший программист старается предусмотреть подобные ситуации. Однако это бывает сложно: перечисленные проблемы обладают неприятным свойством — они могут возникнуть практически в любой момент.
На помощь программисту приходят исключения (exception). Так называют объекты, которые хранят данные о возникшей проблеме. Механизмы исключений в разных языках программирования очень похожи. В зависимости от терминологии языка исключения либо выбрасывают (throw), либо генерируют (raise). Это происходит в тот момент, когда программа не может продолжать выполнять запрошенную операцию.
После выбрасывания в дело вступает системный код, который ищет подходящий обработчик. Особенность в том, что тот, кто выбрасывает исключение, не знает, кто будет его обрабатывать. Может быть, что и вовсе никто — такое исключение останется сиротой и приведёт к падению программы.
Если обработчик всё же найден, то он ловит (catch) исключение и программа продолжает работать как обычно. В некоторых языках вместо catch используется глагол except (исключить).
Обработчик ловит не все исключения, а только некоторые — те, что возникли в конкретной части определённой функции. Эту часть нужно явно обозначить, для чего используют конструкцию try (попробовать). Также обработчик не поймает исключение, которое ранее попало в другой обработчик. После обработки исключения программа продолжает выполнение как ни в чём не бывало.
Исключения: панацея или нет
Перед тем как совершить операцию, нужно убедиться, что она корректна. Если да — совершить эту операцию, а если нет — выбросить исключение. Так делается в некоторых языках, но не в C++. Проверка корректности — это время, а время, как известно, деньги. В C++ считается, что программист знает, что делает, и не нуждается в дополнительных проверках. Это одна из причин, почему программы на C++ такие быстрые.
Но за всё нужно платить. Если вы не уследили и сделали недопустимую операцию, то в менее производительных языках вы получите исключение, а в C++ — неопределённое поведение. Исключение можно обработать и продолжить выполнение программы. Неопределённое поведение гарантированно обработать нельзя.
Но некоторые виды неопределённого поведения вполне понятны и даже могут быть обработаны. Это зависит от операционной системы:
- сигналы POSIX — низкоуровневые уведомления, которые отправляются программе при совершении некорректных операций и в некоторых других случаях;
- структурированные исключения Windows (SEH) — специальные исключения, которые нельзя обработать средствами языка.
Особенность C++ в том, что не любая ошибка влечёт исключение, и не любую ошибку можно обработать. Но если для операции производительность не так критична, почему бы не сделать проверку?
У ряда операций в C++ есть две реализации. Одна супербыстрая, но вы будете отвечать за корректность, а вторая делает проверку и выбрасывает исключение в случае ошибки. Например, к элементу класса std::vector можно обратиться двумя способами:
- vec[15] — ничего не проверяет. Если в векторе нет элемента с индексом 15, вы получаете неопределённое поведение. Это может быть сигнал SIGSEGV, некорректное значение или взрыв компьютера.
- vec.at(15) — то же самое, но в случае ошибки выбрасывается исключение, которое можно обработать.
В C++ вам даётся выбор: делать быстро или делать безопасно. Часто безопасность важнее, но в определённых местах программы любое промедление критично.
Синтаксис исключений в C++
Ловим исключения
Начнём с примера:
В примере есть один try -блок и один catch -блок. Если в блоке try возникает исключение типа ExceptionType , то выполнение блока заканчивается. При этом корректно удаляются созданные объекты — в данном случае переменная var . Затем управление переходит в конструкцию catch . Сам объект исключения передаётся в переменную e . Выводя e.what() , мы предполагаем, что у типа ExceptionType есть метод what .
Если в блоке try возникло исключение другого типа, то управление также прервётся, но поиск обработчика будет выполняться за пределами функции SomeFunction — выше по стеку вызовов. Это также касается любых исключений, возникших вне try -блока.
Во всех случаях объект var будет корректно удалён.
Исключение не обязано возникнуть непосредственно внутри DoSomething*() . Будут обработаны исключения, возникшие в функциях, вызванных из DoSomething* , или в функциях, вызванных из тех функций, да и вообще на любом уровне вложенности. Главное, чтобы исключение не было обработано ранее.
Ловим исключения нескольких типов
Можно указать несколько блоков catch , чтобы обработать исключения разных типов:
Ловим все исключения
Если перед catch(. ) есть другие блоки, то он означает «поймать все остальные исключения». Ставить другие catch -блоки после catch(. ) не имеет смысла.
Перебрасываем исключение
Внутри catch(. ) нельзя напрямую обратиться к объекту-исключению. Но можно перебросить тот же объект, чтобы его поймал другой обработчик:
Можно использовать throw в catch -блоках с указанным типом исключения. Но если поместить throw вне блока catch , то программа тут же аварийно завершит работу через вызов std::terminate() .
Перебросить исключение можно другим способом:
Этот способ обладает дополнительным преимуществом: можно сохранить исключение и перебросить его в другом месте. Однако результат std::current_exception() — это не объект исключения, поэтому его можно использовать только со специализированными функциями.
Принимаем исключение по ссылке
Чтобы избежать лишних копирований, можно ловить исключение по ссылке или константной ссылке:
Это особенно полезно, когда мы ловим исключение по базовому типу.
Выбрасываем исключения
Чтобы поймать исключение, нужно его вначале выбросить. Для этого применяется throw.
Если throw используется с параметром, то он не перебрасывает исключение, а выбрасывает новое. Параметр может быть любого типа, даже примитивного. Использовать такую конструкцию разрешается в любом месте программы:
Вывод: «Поймано исключение типа int, содержащее число –15».
Создаём типы для исключений
Выбрасывать int или другой примитивный тип можно, но это считается дурным тоном. Куда лучше создать специальный тип, который будет использоваться только для исключений. Причём удобно для каждого вида ошибок сделать отдельный класс. Он даже не обязан содержать какие-то данные или методы: отличать исключения друг от друга можно по названию типа.
В итоге будет напечатана только фраза: «Найдено отрицательное число», поскольку –15 проверено раньше нуля.
Ловим исключение по базовому типу
Чтобы поймать исключение, тип обработчика должен в точности совпадать с типом исключения. Например, нельзя поймать исключение типа int обработчиком типа unsigned int .
Но есть ситуации, в которых типы могут не совпадать. Про одну уже сказано выше: можно ловить исключение по ссылке. Есть ещё одна возможность — ловить исключение по базовому типу.
Например, чтобы не писать много catch -блоков, можно сделать все используемые типы исключений наследниками одного. В этом случае рекомендуется принимать исключение по ссылке.
Выбрасываем исключение в тернарной операции ?:
Напомню, что тернарная операция ?: позволяет выбрать из двух альтернатив в зависимости от условия:
Оператор throw можно использовать внутри тернарной операции в качестве одного из альтернативных значений. Например, так можно реализовать безопасное деление:
Это эквивалентно такой записи:
Согласитесь, первый вариант лаконичнее. Так можно выбрасывать несколько исключений в одном выражении:
Вся функция — try-блок
Блок try может быть всем телом функции:
Тут мы просто опустили фигурные скобки функции. По-другому можно записать так:
Исключения в конструкторе
Есть как минимум два случая возникновения исключений в конструкторе объекта:
- Внутри тела конструктора.
- При конструировании данных объекта.
В первом случае исключение ещё можно поймать внутри тела конструктора и сделать вид, как будто ничего не было.
Во втором случае исключение тоже можно поймать, если использовать try-блок в качестве тела конструктора. Однако тут есть особенность: сделать вид, что ничего не было, не получится. Объект всё равно будет считаться недоконструированным:
Тут мы увидим оба сообщения: «Знаменатель дроби не может быть нулём» и «Дробь не построена».
Если объект недоконструирован, то его деструктор не вызывается. Это логичная, но неочевидная особенность языка. Однако все полностью построенные члены – данные объекта будут корректно удалены:
Запустим код и увидим такой вывод:
Объект типа A создался и удалился, а объект типа B создался не до конца и поэтому не удалился.
Не все исключения в конструкторах можно обработать. Например, нельзя поймать исключения, выброшенные при конструировании глобальных и thread_local объектов, — в этом случае будет вызван std::terminate .
Исключения в деструкторе
В этом разделе примера не будет, потому что исключения в деструкторе — нежелательная практика. Бывает, что язык удаляет объекты вынужденно, например, при поиске обработчика выброшенного исключения. Если во время этого возникнет другое исключение в деструкторе какого-то объекта, то это приведёт к вызову std::terminate .
Более того, по умолчанию исключения в деструкторе запрещены и всегда приводят к вызову std::terminate . Выможете разрешить их для конкретного конструктора — об этом я расскажу в следующей части — но нужно много раз подумать, прежде чем сделать это.
Обрабатываем непойманные исключения
Поговорка «не пойман — не вор» для исключений не работает. Непойманные исключения приводят к завершению программы через std::terminate . Это нештатная ситуация, но можно предотвратить немедленное завершение, добавив обработчик для std::terminate :
Однако не стоит надеяться, что программа после обработки такой неприятной ситуации продолжит работу как ни в чём не бывало. std::terminate — часть завершающего процесса программы. Внутри него доступен только ограниченный набор операций, зависящий от операционной системы.
Остаётся только сохранить всё, что можно, и извиниться перед пользователем за неполадку. А затем выйти из программы окончательно вызовом std::abort() .
Базовые исключения стандартной библиотеки
Далеко не всегда есть смысл создавать новый тип исключений, ведь в стандартной библиотеке их и так немало. А если вы всё же создаёте свои исключения, то сделайте их наследниками одного из базовых. Рекомендуется делать все типы исключений прямыми или косвенными наследниками std::exception .
Обратим внимание на одну важную вещь. Все описываемые далее классы не содержат никакой магии. Это обычные и очень простые классы, которые вы могли бы реализовать и самостоятельно. Использовать их можно и без throw , однако смысла в этом немного.
Их особенность в том, что разработчики договорились использовать эти классы для описания исключений, генерируемых в программе. Например, этот код абсолютно корректен, но совершенно бессмысленен:
Разберём основные типы исключений, описанные в стандартной библиотеке C++.
std::exception
Базовый класс всех исключений стандартной библиотеки. Конструктор не принимает параметров. Имеет метод what() , возвращающий описание исключения. Как правило, используются производные классы, переопределяющие метод what() .
std::logic_error : public std::exception
Исключение типа logic_error выбрасывается, когда нарушены условия, сформулированные на этапе написания программы. Например, мы передали в функцию извлечения квадратного корня отрицательное число или попытались извлечь элемент из пустого списка.
Конструктор принимает сообщение в виде std::string , которое будет возвращаться методом what() .
Перечислим некоторые производные классы std::logic_error . У всех них похожий интерфейс.
- std::invalid_argument. Исключение этого типа показывает, что функции передан некорректный аргумент, не соответствующий условиям.
Это исключение выбрасывают функции преобразования строки в число, такие как stol , stof , stoul , а также конструктор класса std::bitset :
- std::length_error. Исключение говорит о том, что превышен лимит вместимости контейнера. Может выбрасываться из методов, меняющих размер контейнеров string и vector . Например resize , reserve , push_back .
- std::out_of_range. Исключение говорит о том, что некоторое значение находится за пределами допустимого диапазона. Возникает при использовании метода at практически всех контейнеров. Также возникает при использовании функций конвертации в строки в число, таких как stol , stof , stoul . В стандартной библиотеке есть исключение с похожим смыслом — std::range_error .
std::runtime_error : public std::exception
std::runtime_error — ещё один базовый тип для нескольких видов исключений. Он говорит о том, что исключение относится скорее не к предусмотренной ошибке, а к выявленной в процессе выполнения.
При этом, если std::logic_error подразумевает конкретную причину ошибки — нарушение конкретного условия, — то std::runtime_error говорит о том, что что-то идёт не так, но первопричина может быть не вполне очевидна.
Интерфейс такой же, как и у logic_error : класс принимает описание ошибки в конструкторе и переопределяет метод what() базового класса std::exception .
Рассмотрим некоторые важные производные классы:
- std::regex_error. Исключение, возникшее в процессе работы с регулярными выражениями. Например, при неверном синтаксисе регулярного выражения.
- std::system_error. Широкий класс исключений, связанных с потоками, вводом-выводом или файловой системой.
- std::format_error. Исключение, возникшее при работе функции std::format .
std::bad_alloc : public std::exception
У std::exception есть и другие наследники. Самый важный — std::bad_alloc . Его может выбрасывать операция new. Это исключение — слабое место многих программ и головная боль многих разработчиков, ведь оно может возникать практически везде — в любом месте, где есть динамическая аллокация. То есть при:
- вставке в любой контейнер;
- копировании любого контейнера, например, обычной строки;
- создании умного указателя unique_ptr или shared_ptr;
- копировании объекта, содержащего контейнер;
- прямом вызове new (надеемся, что вы так не делаете);
- работе с потоками ввода-вывода;
- работе алгоритмов;
- вызове корутин;
- в пользовательских классах и библиотеках — практически при любых операциях.
При обработке bad_alloc нужно соблюдать осторожность и избегать других динамических аллокаций.
Возможный вывод: «Место закончилось после вставки 2640 элементов».
При аллокациях возможна также ошибка std::bad_array_new_length , производная от bad_alloc . Она возникает при попытке выделить слишком большое, слишком маленькое (меньше, чем задано элементов для инициализации) либо отрицательное количество памяти.
Также при аллокации можно запретить new выбрасывать исключение. Для этого пишем (std::nothrow) после new :
В случае ошибки операция будет возвращать нулевой указатель.
bad_alloc настолько сложно учитывать, что многие даже не пытаются это делать. Мотивация такая: если память закончилась, то всё равно программе делать уже нечего. Лучше поскорей вызвать std::terminate и завершиться.
Заключение
В этой части мы разобрали, как создавать исключения C++, какие они бывают и как с ними работать. Разобрали ключевые слова try , catch и throw .
В следующей части запустим бенчмарк, разберём гарантии безопасности, спецификации исключений, а также узнаем, когда нужны исключения, а когда можно обойтись без них. И главное — узнаем, как они работают.
Исключения не так просты, как кажутся на первый взгляд. Они нарушают естественный ход программы и кратно увеличивают количество возможных путей исполнения. Но без них ещё сложнее.
C++ позволяет выразительно обрабатывать исключения, он аккуратен при удалении всех объектов и освобождении ресурсов. Будьте аккуратны и вы, и тогда всё получится. Каждому исключению — по обработчику.
Исключения — это лишь одна из многих возможностей C++. Глубже погрузиться в язык и узнать больше о нём, его экосистеме и принципах программирования поможет курс «Разработчик C++».
Источник