Question on this program with exception C++

  • C/C++
  • Thread starter yungman
  • Start date
  • Tags
    C++ Program
In summary: // 24 39 49 50 51 //
  • #1
yungman
5,718
241
I am stepping through the program and I actually wrote down the steps in comment each step the debugger step through. I have a few question I still don't understand. Here is the program:
C++:
//Matching catch handler with exception
#include<iostream>
#include<string>
#include<string_view>
#include<cstring>
using namespace std;
class Trouble  {public:
    string message;
    Trouble(string_view str = " There is a problem")        // 1    7   16
    {message = str;}                                        // 2    8   17
    virtual ~Trouble()=default;//Base classes must have virtual des.
    virtual string_view getM() const { return message; }};    //                 26        42    53   61

class MoreTrouble : public Trouble
{public: MoreTrouble(string_view str=" Theres more trouble")// 5   14
    : Trouble(str) {}                                       // 6    9   15   18
      ~MoreTrouble() { cout<<" Destructor MoreTrouble.\n\n";}}; //                                    60    62

class BigTrouble : public MoreTrouble
{ public: BigTrouble(string_view str=" Really big trouble")//      12
    : MoreTrouble(str) {};                                //       13        19
      ~BigTrouble() { cout<<" Destructor BigTrouble.\n\n";}};//                                       59    63

int main()
{    Trouble trouble;    //                            Start  3    
    MoreTrouble moreTrouble;                               // 4    10
    BigTrouble bigTrouble;                                 //      11        20
    for (int i = 3; i < 7; ++i)                            //                21   30   36   46        57
    { try                                                  //                22   31   37   47
       {if (i == 3) throw trouble;                         //                23        38   48
        else if (i == 5) throw moreTrouble;                //                          32   39   49
        else if (i == 6) throw bigTrouble;                 //                          33        50
       }                                                   //                                    51
      catch (const BigTrouble& t)
       {cout<<" BigTrouble caught: "<<t.getM()<<"\n\n";}   //                               40   52   54
      catch (const MoreTrouble& t)
       {cout<<" MoreTrouble caught: "<<t.getM()<<"\n\n";}  //                24             41   43
       catch (const Trouble& t)
        {cout<<" Trouble caught: "<<t.getM()<<"\n\n";}    //                 25   27
      cout<<" End of for loop(after catch blocks), i = "<<i<<"\n\n";//            28   34        44   55
    }                                                        //                   29   35        45   56
}//                                                            //                                     58   64

Took me a while to line up the step numbers in straight column so it's easier for you guys to read.
This is the printout with steps labeled:
15.4 stepping.jpg

1) I understand in the first 20 steps, object trouble, moreTrouble and bigTrouble are created. Each inherited the member funtion getM() in line 12 and message in line 8. These are all in the objects already. How come step 25 has to jump to step 26 to access getM() in line 12? this is the same from step 41 jump to step 42. Does the objects have their own getM(), that the program doesn't have to jump to the Trouble class anymore?

2) If you look at the printout with the steps, notice Destructor is called between step 43 and 44. same as from step 54 to 55. I don't see it stepping through the destructor but it said they are executed.

3) Following from question 2 above, you can see destructors are being called again in steps 59, 60 and 61. The program calls the destructors for each object TWICE. Why?

Thanks
 
Technology news on Phys.org
  • #2
I lined up the steps in column in VS program, but when copied over here, it's all out of alignments. I had to spend a lot of time working on the code to make it line up showing in post 1. In case you want to copy and run the program on VS, the one in post 1 will be all over the place in VS.

this is the program copied straight out of VS, I post it in this post so if you want to copy into VS, hopefully it will line up in VS
C++:
//Matching catch handler with exception
#include<iostream>
#include<string>
#include<string_view>
#include<cstring>
using namespace std;
class Trouble  {public:
    string message;
    Trouble(string_view str = " There is a problem")        // 1    7   16
    {message = str;}                                        // 2    8   17
    virtual ~Trouble()=default;//Base classes must have virtual des.
    virtual string_view getM() const { return message; }};    //                 26          42    53   61

class MoreTrouble : public Trouble
{public: MoreTrouble(string_view str=" Theres more trouble")// 5   14
    : Trouble(str) {}                                        // 6    9   15   18
      ~MoreTrouble() { cout<<" Destructor MoreTrouble.\n\n";}}; //                                 60   62

class BigTrouble : public MoreTrouble
{ public: BigTrouble(string_view str=" Really big trouble")    //       12
    : MoreTrouble(str) {};                                    //       13        19
      ~BigTrouble() { cout<<" Destructor BigTrouble.\n\n";}};//                                     59   63

int main()
{    Trouble trouble;    //                                Start  3     
    MoreTrouble moreTrouble;                                // 4   10
    BigTrouble bigTrouble;                                    //       11        20
    for (int i = 3; i < 7; ++i)                                //                 21   30   36   46   57
    { try                                                    //                 22   31   37   47
       {if (i == 3) throw trouble;                            //                 23        38   48
        else if (i == 5) throw moreTrouble;                    //                      32   39   49
        else if (i == 6) throw bigTrouble;                    //                      33        50
       }                                                    //                                51
      catch (const BigTrouble& t) 
       {cout<<" BigTrouble caught: "<<t.getM()<<"\n\n";}    //                           40   52   54
      catch (const MoreTrouble& t)
       {cout<<" MoreTrouble caught: "<<t.getM()<<"\n\n";}    //                 24           41   43
       catch (const Trouble& t) 
        {cout<<" Trouble caught: "<<t.getM()<<"\n\n";}    //                 25   27
      cout<<" End of for loop(after catch blocks), i = "<<i<<"\n\n";//              28   34   44   55
    }                                                        //                      29   35   45   56
}//
 
  • #3
One thing I noticed is that you are using MoreTrouble as a base class (BigTrouble inherits from it), but the MoreTrouble destructor is not virtual.
 
  • #4
yungman said:
1) I understand in the first 20 steps, object trouble, moreTrouble and bigTrouble are created. Each inherited the member funtion getM() in line 12 and message in line 8. These are all in the objects already. How come step 25 has to jump to step 26 to access getM() in line 12? this is the same from step 41 jump to step 42. Does the objects have their own getM(), that the program doesn't have to jump to the Trouble class anymore?
Trouble::getM() is virtual, and it is not overridden by MoreTrouble or BigTrouble. Therefore any call to MoreTrouble::getM() or BigTrouble::getM() is a call to Trouble::getM(). Note that there is no reason to make Trouble::getM() virtual if you are not going to override it in the inheriting classes.
2) If you look at the printout with the steps, notice Destructor is called between step 43 and 44. same as from step 54 to 55. I don't see it stepping through the destructor but it said they are executed.
This is because the throw statements are making copies of the objects they are throwing. In other words, throw trouble is making a copy of trouble and throwing the copy. The copy is destroyed after the corresponding catch is completed.

The reason for this is that objects thrown by throw are always destroyed at the end of the throw/catch. But local variables have a well-defined lifetime: the local variables trouble, moreTrouble, and bigTrouble go out of scope and are destroyed at the end of the scope where they are defined (in this case, at the end of main()). So the throw/catch is not allowed to destroy them and therefore must make copies!
3) Following from question 2 above, you can see destructors are being called again in steps 59, 60 and 61. The program calls the destructors for each object TWICE. Why?
See the explanation above. You are making copies and throwing them, therefore you are seeing the destruction of the copies and then of the originals.

To avoid making copies, don't define these:
C++:
Trouble trouble;
MoreTrouble moreTrouble;
BigTrouble bigTrouble;
Instead create them on demand as part of the throw statement:
C++:
throw Trouble();
throw MoreTrouble();
throw BigTrouble();
The reason this avoids a copy is that Trouble() creates a temporary unnamed object that goes out of scope at the end of the throw statement, so throw doesn't need to make a copy and therefore any reasonable compiler will not make a copy. (I'm not sure if this is guaranteed by the C++ standard.)

Simple example demonstrating the above:
C++:
class A {
public:
    A() { std::cout << "A constructor" << std::endl; }
    A(const A& a) { std::cout << "A copy constructor" << std::endl; }
    ~A() { std::cout << "A destructor" << std::endl; }
};

int main()
{
    {
        A a;
        try {
            throw a;
        }
        catch (const A& aRef) {
            std::cout << "catch1" << std::endl;
        }
    }
    std::cout << "----" << std::endl;
    {
        try {
            throw A();
        }
        catch (const A& aRef) {
            std::cout << "catch2" << std::endl;
        }
    }
    return 0;
}
Output:
Code:
A constructor
A copy constructor
catch1
A destructor
A destructor
----
A constructor
catch2
A destructor
 
Last edited:
  • Like
Likes yungman
  • #5
Thanks a million. I forgot the throw statement made a copy.

But I am stepping through the program, why didn't I see the program stepping to constructor to make the copy, matter of fact, stepping to destructor to destroy the copy from the throw.

You think that's VS problem that doesn't show the steps?

I am still reading your other explanations, I want to take my time and come back again.

Thanks
 
  • #6
jbunniii said:
One thing I noticed is that you are using MoreTrouble as a base class (BigTrouble inherits from it), but the MoreTrouble destructor is not virtual.
I did not see this first reply. No, Trouble is the base class.

BTW, after I posted the response to you, I went back and put the break on the ~MoreTrouble(), It DID stop at that step, but it doesn't display the message "Destructor MoreTrouble" that I put into the destructor. Strange. It's like executing HALF of the destructor!
 
  • #7
yungman said:
I did not see this first reply. No, Trouble is the base class.
MoreTrouble is also being used as a base class!
C++:
class BigTrouble : public MoreTrouble
yungman said:
BTW, after I posted the response to you, I went back and put the break on the ~MoreTrouble(), It DID stop at that step, but it doesn't display the message "Destructor MoreTrouble" that I put into the destructor. Strange. It's like executing HALF of the destructor!
I see the message Destructor MoreTrouble printed three times: see copy of your image below. How many times do you expect to see it?

1612998233024.png

Two possible reasons come to mind for unexpected step behavior using the debugger:

1. Are you compiling for Debug or for Release? If you compile for Release, the debugger will always behave weirdly because the compiler generates optimized assembly code that doesn't align well with the C++ source code.

2. If you're putting the function definition all on one line like this:
C++:
~MoreTrouble() { cout<<" Destructor MoreTrouble.\n\n";}
then the debugger might only stop on that line once, either before or after the message is printed, but maybe not both. Try putting the function body on a separate line and see if the debugger behavior changes:
C++:
~MoreTrouble()
{
    cout<<" Destructor MoreTrouble.\n\n";
}

Regarding the output message, you're using \n instead of std::endl. The latter flushes the stream, but the former does not. So you won't necessarily see the output if you put a breakpoint right after that line. If you change it to
C++:
~MoreTrouble()
{
    cout<<" Destructor MoreTrouble." << std::endl << std::endl;
}
then the message will be printed immediately (at the std::endl).
 
Last edited:
  • #8
jbunniii said:
One thing I noticed is that you are using MoreTrouble as a base class (BigTrouble inherits from it), but the MoreTrouble destructor is not virtual.
Yes, I looked back the original program in the book, it doesn't even have ~MoreTrouble(){}. I added it into output a message. I did not put virtual.

Why is this important to put virtual?

Yes, MoreTrouble is base class for BigTrouble.

thanksEDIT: I tried putting "virtual" in front of ~MoreTrouble(){}, it makes no difference in the output.
 
Last edited:
  • #9
yungman said:
Yes, I looked back the original program in the book, it doesn't even have ~MoreTrouble(){}. I added it into output a message. I did not put virtual.
...
Why is this important to put virtual?
...
EDIT: I tried putting "virtual" in front of ~MoreTrouble(){}, it makes no difference in the output.
Yes, in your case it doesn't make a difference because you're not managing a BigTrouble object via a pointer to MoreTrouble. An example where it would matter is as follows:
C++:
// Perfectly legal to use BigTrouble
// via a pointer to its base class MoreTrouble
MoreTrouble* pMT = new BigTrouble;
...
// But now when we want to destroy the object, this will invoke
// ~MoreTrouble, not ~BigTrouble, even though the object is of type
// BigTrouble! This is because ~MoreTrouble is not virtual.
// If BigTrouble allocated any resources that need to be freed in
// ~BigTrouble, then they will not be freed and there will be
// a resource leak! (e.g. memory leak)
delete pMT;
By the way, the problem isn't limited to dumb pointers. The same situation occurs if you use a smart pointer:
C++:
int main()
{
    std::unique_ptr<MoreTrouble> pmt = std::make_unique<BigTrouble>();
    ...
    // use the BigTrouble object via a MoreTrouble pointer, perfectly OK
    ...
    // but now at the end of scope, the unique pointer is destroyed,
    // and this calls ~MoreTrouble instead of ~BigTrouble because
    // ~MoreTrouble is not virtual.
}
 
  • #10
jbunniii said:
2. If you're putting the function definition all on one line like this:
C++:
~MoreTrouble() { cout<<" Destructor MoreTrouble.\n\n";}
then the debugger might only stop on that line once, either before or after the message is printed, but maybe not both. Try putting the function body on a separate line and see if the debugger behavior changes:
C++:
~MoreTrouble()
{
    cout<<" Destructor MoreTrouble.\n\n";
}
This has been explained previously in other threads on why it isn't a good idea to compress the code...
The debugger doesn't step through multiple statements on a single line, including code where the function header and its body on a single line.
 
  • #11
Thanks for the replies
I change this:
C++:
~MoreTrouble()
    { cout<<" Destructor MoreTrouble.\n\n";}
Still jump right pass it.
 
  • #12
I tried again, this time I did it like this.
C++:
{public: MoreTrouble(string_view str=" Theres more trouble")// 5   14
    : Trouble(str) {}                                        // 6    9   15   18
      ~MoreTrouble()
      {
          cout<<" Destructor MoreTrouble.\n\n";
      }

Exactly the same thing. Doesn't make a dent of a difference.

debugger in VS just doesn't step through it. This is really my question.

Seems like it's VS.
 
  • #13
What if you put a breakpoint on the line
C++:
cout<<" Destructor MoreTrouble.\n\n";
Does the debugger stop there if you do that?
 
  • #14
jbunniii said:
What if you put a breakpoint on the line
C++:
cout<<" Destructor MoreTrouble.\n\n";
Does the debugger stop there if you do that?
Yes.

I did try to break on the destructor, it will break. I am talking about if I step through the program, it will not stop at the destructor, it just skip pass it like I described in the first post.

Like I said, it will print the message in the destructor, but it will not stop at the destructor. I am starting to think it's VS at this point.

What you responded in post 3 makes sense, maybe I should let it go at this point. It obviously did invoked the destructors like you said that the throw made a copy and it will be destroyed as soon as the throw and catch is done.

I don't know why MoreTrouble was destroyed 3 times. That's a good question.

Thanks
 
  • #15
If you're stepping through a program in VS with F10 or the Step Over menu item under Debug, VS won't go into function bodies. To see better what's happening use F11 or Step Into menu item to get VS to show you what happens inside functions.
 
  • #16
Mark44 said:
If you're stepping through a program in VS with F10 or the Step Over menu item under Debug, VS won't go into function bodies. To see better what's happening use F11 or Step Into menu item to get VS to show you what happens inside functions.
I always use F11, I don't even know about F10.

Thanks
 
  • #17
The best way I know of to see if a certain line of code is executing is to put a breakpoint on that line. Also, for very small programs, I might step through the code line by line, but for longer ones I'll put a breakpoint at the place where I want to start looking more closely. Then I hit F5 to get to that breakpoint and then use either F10 or F11, depending on whether I need to trace into function calls or not.
 
  • Like
Likes yungman
  • #18
Mark44 said:
The best way I know of to see if a certain line of code is executing is to put a breakpoint on that line. Also, for very small programs, I might step through the code line by line, but for longer ones I'll put a breakpoint at the place where I want to start looking more closely. Then I hit F5 to get to that breakpoint and then use either F10 or F11, depending on whether I need to trace into function calls or not.
I did mention in post 6, if I put a break point on the destructor, it WILL stop at the destructor.

My question is when I step through the program ( break anywhere before the destructor and step with F11), then it will not stop at the destructor. BUT it will still printout the message of the detructor(meaning it did execute the destructor). This is really for my information, why the stepping won't stop at the destructor that was clearly executed.Thanks
 
Last edited:
  • #19
No idea, but possibly the compiler is inlining the destructor code. What I do know is that in any of your catch blocks, after the output statement, there is a whole lot of code that executes (looking at the disassembly code). Maybe someone here knows the answer to your question, or maybe not. My advice is that if the debugger doesn't seem to be stopping at a line, but it executes anyway, put a breakpoint there.
 
  • #20
Mark44 said:
No idea, but possibly the compiler is inlining the destructor code. What I do know is that in any of your catch blocks, after the output statement, there is a whole lot of code that executes (looking at the disassembly code). Maybe someone here knows the answer to your question, or maybe not. My advice is that if the debugger doesn't seem to be stopping at a line, but it executes anyway, put a breakpoint there.
Thanks Mark

If you don't know, that's good enough for me as you are about the most knowledgeable person.( Now I don't mean to put down anyone here, I just say Mark is very very knowledgeable)That's why I start to wonder maybe I should let this go. Maybe I am looking too close, having too much fun learning from Jtbell that time and keep looking at where the program goes exactly. I sometimes even go further and look at the addresses also to verify. Over all, the program is doing exactly what it supposed to do.

Thanks
 

1. What is an exception in C++?

An exception in C++ is an error or unexpected behavior that occurs during the execution of a program. It can happen when the program encounters a problem that it cannot handle, such as division by zero or trying to access a non-existent file.

2. How do I handle exceptions in C++?

C++ provides a mechanism called try-catch blocks for handling exceptions. The code that may throw an exception is placed inside the try block, and the code that handles the exception is placed inside the catch block. If an exception is thrown, the program jumps to the catch block and executes the code there.

3. Can I create my own exception in C++?

Yes, you can create your own exception class in C++. This can be useful when you want to handle specific types of errors in your program. You can define your own custom exception class by inheriting from the std::exception class.

4. What is the difference between a compile-time and a run-time exception?

A compile-time exception occurs during the compilation of the program and is usually caused by syntax errors or type mismatches. A run-time exception, on the other hand, occurs during the execution of the program and is usually caused by unexpected inputs or errors in the program logic.

5. How do I prevent exceptions from crashing my program?

You can prevent exceptions from crashing your program by using try-catch blocks to handle them. Additionally, you can use exception handling techniques such as defensive programming and input validation to prevent exceptions from occurring in the first place.

Similar threads

  • Programming and Computer Science
2
Replies
40
Views
2K
  • Programming and Computer Science
2
Replies
35
Views
2K
  • Programming and Computer Science
3
Replies
89
Views
4K
  • Programming and Computer Science
Replies
3
Views
1K
  • Programming and Computer Science
3
Replies
89
Views
4K
  • Programming and Computer Science
Replies
5
Views
818
  • Programming and Computer Science
Replies
30
Views
2K
  • Programming and Computer Science
2
Replies
36
Views
2K
  • Programming and Computer Science
Replies
12
Views
1K
  • Programming and Computer Science
3
Replies
73
Views
4K
Back
Top