본문 바로가기
College Study/C++ (ENG)

[C++] Exception (ENG)

by 2den 2022. 1. 2.
728x90

 

Out Of Range

#include <exception>

int main()
{
    std::string myCatName = "Coco";

    try
    {
        char ch = myCatName.at(5);
    }
    catch (const std::out_of_range& e)
    {
        std::cerr << "out of range: " << e.what() << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << "exception: " << e.what() << std::endl;
    }

    return 0;
}
 

You can handle this 'out of range' problem without try/catch.

std::string myCatName = "Coco";

const size_t INDEX = 5;
if (INDEX < myCatName.size())
{
    char ch = myCatName.at(INDEX);
    // ...
}
 

 

 

 

Division By Zero(0)

int number1 = 0;
int number2 = 0;

std::cin >> number1 >> number2;

try
{
    int result = number1 / number2;
}
catch (const std::exception& e)
{
    std::cerr << "exception: " << e.what() << std::endl;
}
 

You can handle this 'division by zero' problem without try/catch.

int number1;
int number2;

std::cin >> number1 >> number2;

if (number2 != 0)
{
    int result = number1 / number2;
}
 

 

 

 

NULL Object

Cat* myCat = nullptr;
// ...

try
{
    const char* myCatName = myCat->GetName();
    std::cout << myCatName << std::endl;
}
catch (const std::exception& e)
{
    std::cerr << "exception: " << e.what() << std::endl;
}
 

You can handle this 'NULL object' problem without try/catch. NULL implies failure/success. Every reference type can indicate not only objects but fail/success.

 

 

 

Exception of OS and C++

OS "Exception"
std::exception
Asynchronous
Synchronous
Window
SEH
C++ STL
Linux
POSIX sign, Faults, Traps, Aborts
Depends on Platform
Common to All Platforms
More Overhead, Slow
Less Overhead

 

 

 

Exceptions in the Constructor

// Exceptions.h
#include <exception>

struct SlotNullException : public std::exception
{
    const char* what() const throw ()
    {
        return "Slot is NULL";
    }
};
 
// Inventory.cpp
Inventory::Inventory(int slotCount)
{
    mSlots = new int[slotCount];
    if (mSlots == NULL)
    {
        throw SlotNullException();
    }
}
 
// Main.cpp
Inventory* myInventory = nullptr;
try
{
    myInventory = new Inventory(5);
}
catch (const SlotNullException& e)  // should prevent using nullptr again (not completed)
{
    std::cerr << e.what() << std::endl;
}
catch (const std::exception& e)
{
    // other errors
}
 

Exceptions in the constructor are fine. However, turning off exception handling is the default option for many C++ compilers. Because using exception handling is slow. You may get an exception due to lack of memory. Then, closing the program is no different from crashing, and retrying the constructor call will not solve the problem because the memory is already exhausted.

 

 

Error Codes of C

#define SUCESS (0x0)
#define FAILED_OPEN_FILES (0x1)

int PrintAllRecord()
{
    FILE *fp = NULL;

    fp = fopen(FILE_NAME, "r");
    if (fp == NULL)
    {
        return FAILED_OPEN_FILES;
    }
    // ...
    return SUCCESS;
}

int main()
{
    int result = PrintAllRecord();  // geterrorcode();

    if (result != SUCCESS)
    {
        fprintf(stderr, "Can't print records.\n");
    }

    return 0;
}
 

cin.fail( ) is a kind of error code.

int number;
cin >> number;

if (cin.fail())
{
    // ...
}
 

 

 

 

Exceptions are Better than Error Codes?

Error codes are not less readable.

// Error code
public void sendShutDown()
{
    boolean bSuccess = trySendShutDown();
    if (!bSuccess)
    {
        print // ...
    }
}

// Exception
public void sendShutDown() {
    try {
        tryToShutDown();
    } catch (DeviceShutDownError e) {
        logger.log(e);
    }
}
 

Use the early exit.

public class DeviceController {
    public void sendShutDown() {
        DeviceHandle handle = getHandle(DEV1);
        // check the status of the device
        if (handle == DeviceHandle.INVALID) {
            logger.log("Invalid handle for: " + DEV1.toString());
            return;
        }

        // store the state of the device in the record field
        retrieveDeviceRecord(handle);
        // exit if not suspended
        if (record.getStatus() == DEVICE_SUSPENDED) {
            logger.log("Device suspended. Unable to shut down");
            return;
        }
        pauseDevice(handle);
        clearDeviceWorkQueue(handle);
        closeDevice(handle);
    }
    // ...
}
 

Proper Exception Handling

1. Validation and exceptions only on boundaries

You can't control data coming from outside. (external web request, file read/write, external library)

// C#
string ReadFileOrNull(string filename)
{
    if (!File.Exists(filename))
    {
        return null;
    }

    try
    {
        return File.LoadAllText(filename);
    }
    catch (Exception e)
    {
        return null;
    }
}
 

2. Assume that all data once entered into the system is correct.

Use assert to catch and fix problems during development.

int ConvertToHumanAge(const Animal* pet)
{
    Assert(pet != NULL);
    // ...
}
 

3. Use NULL when exceptions occur.

But basically the function should not return or receive NULL. Name functions well if they return or receive NULL.

// if the parameter can be NULL
const char* GetCollName(const char* starWithOrNull) const;

// if the function can return NULL
const char* GetHobbyOrNull() const;
 

4. Catch bugs during development

Don't do it on live servers. With assert, you can check preconditions, postconditions, and invariant, and also can check the correct call stack on failure. If the quality assurance (QA) is not done properly, it is better to make the software stretch out. Without exceptions, you can get and dump a memory dump file when a program crashed.

 

728x90

댓글