简体   繁体   中英

C++ syntax I don't understand

I've found a C++ code that has this syntax:

void MyClass::method()
{
    beginResetModel();

    {
        // Various stuff
    }

    endResetModel();
}

I've no idea why there are { } after a line ending with ; but it seems there is no problem to make it compile and run. Is it possible this as something to do with the fact that the code may be asynchronous (I'm not sure yet)? Or maybe the { } are only here to delimit a part of the code and don't really make a difference but honestly I doubt this. I don't know, does someone has any clue what this syntax mean ?

More info: There is no other reference to beginResetModel , resetModel or ResetModel in the whole project (searched with grep). Btw the project is a Qt one. Maybe it's another Qt-related macro I haven't heard of.

Using {} will create a new scope . In your case, any variable created in those braces will cease to exist at the } in the end.

  beginResetModel(); { // Various stuff } endResetModel() 

The open and close braces in your code are a very important feature in C++, as they delimit a new scope . You can appreciate the power of this in combination with another powerful language feature: destructors .

So, suppose that inside those braces you have code that creates various objects, like graphics models, or whatever.

Assuming that these objects are instances of classes that allocate resources (eg textures on the video card), and those classes have destructors that release the allocated resources, you are guaranteed that, at the } , these destructors are automatically invoked.

In this way, all the allocated resources are automatically released, before the code outside the closing curly brace, eg before the call to endResetModel() in your sample.

This automatic and deterministic resource management is a key powerful feature of C++.


Now, suppose that you remove the curly braces, and your method looks like this:

void MyClass::method()
{
    beginResetModel();

    // {
        // Various stuff
    // }

    endResetModel();
}

Now, all the objects created in the Various stuff section of code will be destroyed before the } that terminates the MyClass::method() , but after the call to endResetModel() .

So, in this case, you end up with the endResetModel() call, followed by other release code that runs after it. This may cause bugs. On the other hand, the curly braces that define a new scope enclosed in begin/endResetModel() do guarantee that all the objects created inside this scope are destroyed before endResetModel() is invoked.

{} delimits a scope. That means that any variable declared inside there is not accessible outside of it and is erased from memory once the } is reached. Here is an example:

#include <iostream>

using namespace std;

class MyClass{
public:
    ~MyClass(){
        cout << "Destructor called" << endl;
    }
};

int main(){
    {
        int x = 3;
        MyClass foo;
        cout << x << endl;    //Prints 3
    }    //Here "Destructor called" is printed since foo is cleared from the memory

    cout << x << endl;    //Compiler error, x isn't defined here

    return 0;
}

Usually scopes are used for functions, loops, if-statements, etc, but you're perfectly allowed to use scopes without any statement before them. This can be particularly useful to declare variables inside a switch ( this answer explains why).

As others have pointed out, the curly braces create a new scope, but maybe the interesting thing is why would you want to do that - that is, what is the difference between using it and not using it. There are cases where scopes are obviously necessary, such as with if or for blocks; if you don't create a scope after them you can only have one statement. Another possible reason is that maybe you use one variable in one part of the function and do not one it to be used outside of that part, so you put it into its own scope. However, the main use of scopes out of control statements has to do with RAII . When you declare an instance variable (not a pointer or reference), it is always initialized; when it goes out of scope, it is always destroyed. This can be used to define blocks that require some setup at the beginning and some tear down at the end (if you are familiar with Python, similar to with blocks).

Take this example:

#include <mutex>

void fun(std::mutex & mutex) {
    // 1. Perform some computations...
    {
        std::lock_guard<std::mutex> lock(mutex);
        // 2. Operations in this scope are performed with the mutex locked
    }
    // 3. More computations...
}

In this example, part 2 is only run after the mutex has been acquired, and is released before part 3 starts. If you remove the additional scope:

#include <mutex>

void fun(std::mutex & mutex) {
    // 1. Perform some computations...
    std::lock_guard<std::mutex> lock(mutex);
    // 2. Operations in this scope are performed with the mutex locked
    // 3. More computations...
}

In this case the mutex is acquired before starting part 2, but it is held until part 3 is complete (possibly producing more interlocking between threads than necessary). Note, however, that in both cases there was no need to specify when the lock is released; std::lock_guard is responsible for both acquiring the lock on construction and releasing it on destruction (ie when it goes out of scope).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM