Non-Modifying Algorithms

#include <algorithm>

for_each

#include <algorithm>
...
template <typename T>
class ContInfo{
public:
  void operator()(T t){
    num++;
    sum+= t;
  }
  int getSum() const{ return sum; }
  int getSize() const{ return num; }
  double getMean() const{
    return static_cast<double>(sum)/static_cast<double>(num);
  }
private:
  T sum{0};
  int num{0};
};

std::vector<double> myVec{1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};
auto vecInfo= std::for_each(myVec.begin(), myVec.end(), ContInfo<double>());

std::cout << vecInfo.getSum() << std::endl;    // 49
std::cout << vecInfo.getSize() << std::endl;   // 9
std::cout << vecInfo.getMean() << std::endl;   // 5.5

std::array<int, 100> myArr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto arrInfo= std::for_each(myArr.begin(), myArr.end(), ContInfo<int>());

std::cout << arrInfo.getSum() << std::endl;    // 55
std::cout << arrInfo.getSize() << std::endl;   // 100
std::cout << arrInfo.getMean() << std::endl;   // 0.55

Search Elements

std::find, std::find_if, std::find_if_not, std::find_of, and std::adjacent_find

bool isVowel(char c){
  string myVowels{"aeiouäöü"};
  set<char> vowels(myVowels.begin(), myVowels.end());
  return (vowels.find(c) != vowels.end());
}

list<char> myCha{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
int cha[]= {'A', 'B', 'C'};

cout << *find(myCha.begin(), myCha.end(), 'g');             // g
cout << *find_if(myCha.begin(), myCha.end(), isVowel);      // a
cout << *find_if_not(myCha.begin(), myCha.end(), isVowel);  // b

auto iter= find_first_of(myCha.begin(), myCha.end(), cha, cha + 3);
if (iter == myCha.end()) cout << "None of A, B or C.";      // None of A, B or C.
auto iter2= find_first_of(myCha.begin(), myCha.end(), cha, cha+3, 
            [](char a, char b){ return toupper(a) == toupper(b); });

if (iter2 != myCha.end()) cout << *iter2;                   // a
auto iter3= adjacent_find(myCha.begin(), myCha.end());
if (iter3 == myCha.end()) cout << "No same adjacent chars."; 
                                        // No same adjacent chars.

auto iter4= adjacent_find(myCha.begin(), myCha.end(),
            [](char a, char b){ return isVowel(a) == isVowel(b); });
if (iter4 != myCha.end()) cout << *iter4;                   // b 
struct A{
    int v;
    bool operator==(A other){
        return other.v==v;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc,argv);
    QList<A> list{{1},{2}};
    auto it =std::find(list.begin(),list.end(),A{2});
    if(it==list.end())
        qDebug()<<"can not found it";
    else
        qDebug()<<(*it).v;
    return a.exec();
}
struct A{
    int v;
    bool operator==(A other){
        return other.v==v;
    }
};

struct comparator{
    bool operator()( A& a1, A& a2){
        qDebug()<<a1.v<<a2.v;
        return false;
    }
};


int main(int argc, char *argv[])
{
    QCoreApplication a(argc,argv);
    QList<A> list{{1},{2},{3},{4},{5}};
    auto it =std::adjacent_find(list.begin(),list.end(),comparator());//(1,2),(2,3),(3,4),(4,5)
    return a.exec();
}

Count Elements

std::count, and std::count_if

std::string str{"abcdabAAAaefaBqeaBCQEaadsfdewAAQAaafbd"};
std::cout << std::count(str.begin(), str.end(), 'a');              // 9
std::cout << std::count_if(str.begin(), str.end(),              
                           [](char a){ return std::isupper(a); }); // 12

Check Conditions on Ranges

std::all_of, std::any_of, and std::none_of

auto even= [](int i){ return i%2; };
std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::cout << std::any_of(myVec.begin(), myVec.end(), even);  // true
std::cout << std::all_of(myVec.begin(), myVec.end(), even);  // false
std::cout << std::none_of(myVec.begin(), myVec.end(), even); // false

Compare Ranges

equal , mismatch

string str1{"Only For Testing Purpose."};
string str2{"only for testing purpose."};
cout << equal(str1.begin(), str1.end(), str2.begin());  // false
cout << equal(str1.begin(), str1.end(), str2.begin(),
              [](char c1, char c2){ return toupper(c1) == toupper(c2);} ); 
			                                          // true
str1= {"Only for testing Purpose."};
str2= {"Only for testing purpose."};
auto pair= mismatch(str1.begin(), str1.end(), str2.begin());
if (pair.first != str1.end()){
  cout << distance(str1.begin(), pair.first)
       << "at (" << *pair.first << "," << *pair.second << ")";  // 17 at (P,p)
}
auto pair2= mismatch(str1.begin(), str1.end(), str2.begin(),
                     [](char c1, char c2){ return toupper(c1) == toupper(c2); });
if (pair2.first == str1.end()){
  cout << "str1 and str2 are equal";  // str1 and str2 are equal
}

Search for Ranges within Ranges

search : Searches the second range in the first one and returns the position. Starts at the beginning

find_end : Searches the second range in the first one and returns the positions. Starts at the end

search_n : Searches count consecutive values in the first range:

std::array<int, 10> arr1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::array<int, 5> arr2{3, 4, -5, 6, 7};

auto fwdIt= search(arr1.begin(), arr1.end(), arr2.begin(), arr2.end());
if (fwdIt == arr1.end()) std::cout << "arr2 not in arr1.";  // arr2 not in arr1.

auto fwdIt2= search(arr1.begin(), arr1.end(), arr2.begin(), arr2.end(),
                    [](int a, int b){ return std::abs(a) == std::abs(b); });
if (fwdIt2 != arr1.end()) std::cout << "arr2 at position "
                    << std::distance(arr1.begin(), fwdIt2) << " in arr1."; 
                                                  // arr2 at position 3 in arr1.

parallel Algorithms

#include <iostream>
#include <vector>
#include <execution>
#include <algorithm>
#include <stdlib.h>
#include<time.h>
#include<chrono>

int main(int argc,const char* argv[]){
    std::srand(time(nullptr));
    std::vector<double> values;
    for(int i=0;i<=9999999;i++){
        values.push_back(rand()%9999);
    }
    std::chrono::time_point<std::chrono::system_clock> tf=std::chrono::system_clock::now();
    std::for_each(std::execution::par_unseq,values.begin(),values.end(),[](double& el){el=el/12.0+120.0;});
    int res=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()-tf).count();
    std::cout<<res;


    return 0;
}

to use the execution library with Linux you have to install libtbb-dev and add -ltbb flag to the compiler

Smart Pointers

#include <memory>  

std::unique_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};
void process_item(unique_ptr<Foo> p)
{
   if (!p) { return; }
   cout << "Processing " << p->name << '\n';
}
int main()
{
    {
        unique_ptr<Foo> p1 {new Foo{"foo"}};
        auto            p2 (make_unique<Foo>("bar"));
    }
    process_item(make_unique<Foo>("foo1"));
    auto p1 (make_unique<Foo>("foo2"));
    auto p2 (make_unique<Foo>("foo3"));
    process_item(move(p1));//unique_ptr have only move constructor and move assigment
    cout << "End of main()\n";
}

After we left the scope, both objects are destructed immediately and their memory is released to the heap. Let’s have a look at the process_item function and how to use it with unique_ptr now. If we construct a new Foo instance, managed by a unique_ptr in the function call, then its lifetime is reduced to the scope of the function. When process_item returns, the object is destroyed
f we want to call process_item with an object that already existed before the call, then we need to transfer ownership because that function takes a unique_ptr by value, which means that calling it would lead to a copy. But unique_ptr cannot be copied, it can only be moved. Let’s create two new Foo objects and move one into process_item. By looking at the terminal output later, we will see that foo2 is destroyed when process_item returns because we transferred ownership to it. foo3 will continue living until the main function returns

CTOR foo
CTOR bar
DTOR bar
DTOR foo
CTOR foo1
Processing foo1
DTOR foo1
CTOR foo2
CTOR foo3
Processing foo2
DTOR foo2
End of main()
DTOR foo3

std::shared_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void f(shared_ptr<Foo> sp)
{
      cout << "f: use counter at " << sp.use_count() << '\n';
}
int main()
{
   shared_ptr<Foo> fa;
   {
        cout << "Inner scope begin\n";
        shared_ptr<Foo> f1 {new Foo{"foo"}};
        auto            f2 (make_shared<Foo>("bar"));
        cout << "f1's use counter at " << f1.use_count() << '\n';
        fa = f1;
        cout << "f1's use counter at " << f1.use_count() << '\n';
   }
    cout << "Back to outer scope\n";
    cout << fa.use_count() << '\n';
    cout << "first f() call\n";
    f(fa);
    cout << "second f() call\n";
    f(move(fa));
    cout << "end of main()\n";
}
Inner scope begin
CTOR foo
CTOR bar
f1's use counter at 1
f1's use counter at 2
DTOR bar
Back to outer scope
1
first f() call
f: use counter at 2
second f() call
f: use counter at 1
DTOR foo
end of main()

std::weak_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void weak_ptr_info(const weak_ptr<Foo> &p)
{
   cout << "---------" << boolalpha
   << "\nexpired:   " << p.expired()
   << "\nuse_count: " << p.use_count()
   << "\ncontent:   ";
   if (const auto sp (p.lock()); sp) {
      cout << sp->value << '\n';
   } else {
      cout << "<null>\n";
   }
}

The expired function of weak_ptr tells us if the object it points to still really exists, because holding a weak pointer to an object does not prolong its lifetime! The use_count counter tells us how many shared_ptr instances are currently pointing to the object in question:

int main()
{
   weak_ptr<Foo> weak_foo;
   weak_ptr_info(weak_foo);
   {
        auto shared_foo (make_shared<Foo>(1337));
        weak_foo = shared_foo;
        weak_ptr_info(weak_foo);
   }
   weak_ptr_info(weak_foo);
}
---------
expired: true
use_count: 0
content: <null>
---------
expired: false
use_count: 1
content: 1337
DTOR Foo 1337
---------
expired: true
use_count: 0
content: <null>

Placement new operator in C++

Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory.

Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.

new vs placement new

  • Normal new allocates memory in heap and constructs objects tehre whereas using placement new, object construction can be done at known address.
  • With normal new, it is not known that, at what address or memory location it’s pointing to, whereas the address or memory location that it’s pointing is known while using placement new.
  • The deallocation is done using delete operation when allocation is done by new but there is no placement delete, but if it is needed one can write it with the help of destructor
new (address) (type) initializer

When to prefer using placement new ?

As it allows to construct an object on memory that is already allocated , it is required for optimizations as it is faster not to re-allocate all the time. There may be cases when it is required to re-construct an object multiple times so, placement new operator might be more efficient in these cases.

#include<iostream> 
using namespace std; 
  
int main() 
{ 
    // buffer on stack 
    unsigned char buf[sizeof(int)*2] ; 
  
    // placement new in buf 
    int *pInt = new (buf) int(3); 
  
    int *qInt = new (buf + sizeof (int)) int(5); 
    int *pBuf = (int*)(buf+0) ; 
    int *qBuf = (int*) (buf + sizeof(int)); 
    cout << "Buff Addr             Int Addr" << endl; 
    cout << pBuf <<"             " << pInt << endl; 
    cout << qBuf <<"             " << qInt << endl; 
    cout << "------------------------------" << endl; 
    cout << "1st Int             2nd Int" << endl; 
    cout << *pBuf << "                         "
         << *qBuf << endl; 
  
    return 0; 
}
This image has an empty alt attribute; its file name is 1-7.pngThis image has an empty alt attribute; its file name is 2-3.png
#include<iostream> 
using namespace std; 
int main() 
{ 
    // initial value of X 
    int X = 10; 
  
    cout << "Before placement new :" << endl; 
    cout << "X : " << X << endl; 
    cout << "&X : " << &X << endl; 
  
    // Placement new changes the value of X to 100 
    int *mem = new (&X) int(100); 
  
    cout << "\nAfter placement new :" << endl; 
    cout << "X : " << X << endl; 
    cout << "mem : " << mem << endl; 
    cout << "&X : " << &X << endl; 
  
    return 0; 
} 
Before placement new :
X : 10
&X : 0x69fee8

After placement new :
X : 100
mem : 0x69fee8
&X : 0x69fee8

How to delete the memory allocated by placement new ?

he operator delete can only delete the storage created in heap, so when placement new is used delete operator cannot be used to delete the storage. In the case of memory allocation using placement new operator , since it is created in stack the compiler knows when to delete it and it will handle deallocation of the memory automatically. If required, one can write it with the help of destructor as shown below.

#include<iostream> 
#include<cstdlib> 
#include<cmath> 
using namespace std; 
  
class Complex 
{ 
private: 
    double re_, im_; 
public: 
    // Constructor 
    Complex(double re = 0, double im = 0): re_(re), im_(im) 
    { 
        cout << "Constructor : (" << re_ 
             << ", " << im_ << ")" << endl; 
    } 
  
    // Destructor 
    ~Complex() 
    { 
        cout << "Destructor : (" << re_ << ", "
             << im_ << ")" << endl; 
    } 
  
    double normal() 
    { 
        return sqrt(re_*re_ + im_*im_); 
    } 
  
    void print() 
    { 
        cout << "|" << re_ <<" +j" << im_ 
             << " | = " << normal() << endl; 
    } 
}; 
  
// Driver code 
int main() 
{ 
    // buffer on stack 
    unsigned char buf[100]; 
  
    Complex* pc = new Complex(4.2, 5.3); 
    Complex* pd = new Complex[2]; 
  
    // using placement new 
    Complex *pe = new (buf) Complex(2.6, 3.9); 
  
    // use objects 
    pc -> print(); 
    pd[0].print(); 
    pd[1].print(); 
    pe->print(); 
  
    // Release objects 
    // calls destructor and then release memory 
    delete pc; 
  
    // Calls the destructor for object pd[0] 
    // and then release memory 
    // and it does same for pd[1] 
    delete [] pd; 
  
    // No delete : Explicit call to Destructor. 
    pe->~Complex(); 
  
    return 0; 
} 
Constructor : (4.2, 5.3)
Constructor : (0, 0)
Constructor : (0, 0)
Constructor : (2.6, 3.9)
|4.2 +j5.3 | = 6.7624
|0 +j0 | = 0
|0 +j0 | = 0
|2.6 +j3.9 | = 4.68722
Destructor : (4.2, 5.3)
Destructor : (0, 0)
Destructor : (0, 0)
Destructor : (2.6, 3.9)

When will placement new operator show segmentation fault ?

The placement new operator should be used with care. The address which is passed can be a reference or a pointer  pointing to a valid memory location. It may show error when the address passed is :

  • A pointer such as NULL pointer.
  • A pointer that is not pointing to any location.
  • It cannot be a void pointer unless it points to some location.
#include<iostream> 
using namespace std; 
  
int main() 
{ 
    // Fine 
    int i = 10; 
    int *ipt = &i ; 
    int *i1 = new(ipt) int(9) ; 
  
    // Incorrect as ip may not 
    // be a valid address 
    int *ip; 
    int *i2 = new(ip) int(4) ; 
  
    // Fine 
    void *vd = &i; 
    int *i3 = new(vd) int(34) ; 
  
    // Incorrect as x is not an address 
    int x; 
    int *i5 = new(x) int(3) ; 
  
    return 0; 
} 

Advantages of placement new operator over new operator

  • The address of memory allocation is known before hand.
  • Useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount.
  • There’s no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time.
  • This feature becomes useful while working in an environment with limited resources.

Qt Meta Type

The QMetaType class manages named types in the meta-object system

The class is used as a helper to marshall types in QVariant and in queued signals and slots connections. It associates a type name to a type so that it can be created and destructed dynamically at run-time. Declare new types with Q_DECLARE_METATYPE() to make them available to QVariant and other template-based functions. Call qRegisterMetaType() to make types available to non-template based functions, such as the queued signal and slot connections.

Q_DECLARE_METATYPE(MyStruct)
MyStruct s;
QVariant var;
var.setValue(s); // copy s into the variant
// retrieve the value
MyStruct s2 = var.value<MyStruct>();

what can you do once you register type with meta types?

  • create object with type id
  • value as Qvariant
  • streaming data of object (save ,load ,<<,>>,…)(qRegisterMetaTypeStreamOperators<type>(type_name))
  • pass values betweens queued signals and slots connections

struct Test2
{
    int i;
};
Q_DECLARE_METATYPE(Test2);

QDataStream &operator<<(QDataStream &out, const Test2 &myObj)
{
    out<<myObj.i;
    return out;
}

QDataStream &operator>>(QDataStream &in, Test2 &myObj)
{
    in>>myObj.i;
    return in;
}

qRegisterMetaTypeStreamOperators<Test2>("Test2");

QVariant var1;
Test2 t1{.i=45};
var1.setValue(t1);
QFile f("./hello.txt");
f.open(QIODevice::WriteOnly);
QDataStream ds(&f);
ds<<var1;
f.close();

QVariant var2;
Test2 t2;
var2.setValue(t2);
f.open(QIODevice::ReadOnly);
ds>>var2;
f.close();
 qDebug()<<"value is : "<<var2.value<Test2>().i;//45
qMetaTypeId<type>()//return type id of type
QMetaType::fromType<type>()//return QMetaType 
QMetaType::type("Test2");//return type id by type name

or you can use member functions

QMetaObject* metaObject();// if it subclass of QObject return meta object
void* create(const void *copy = 0)// return pointer for copy of copy
void* construct(void *where, const void *copy = 0)// create copy of copy with determined adress (where)
destroy(void*)//
destruct(int type,void* where)// call destruct function without delete operator

QVariant

//before you can use any type as variant you sould to use Q_DECLARE_METATYPE
qvariant_cast<Type>(variant);//cast variant to type
variant.value<Type>();//cast value to type

Preprocessor directives

Preprocessor directives are lines included in the code of programs preceded by a hash sign (#). These lines are not program statements but directives for the preprocessor. The preprocessor examines the code before actual compilation of code begins and resolves all these directives before any code is actually generated by regular statements.

These preprocessor directives extend only across a single line of code. As soon as a newline character is found, the preprocessor directive is ends. No semicolon (;) is expected at the end of a preprocessor directive. The only way a preprocessor directive can extend through more than one line is by preceding the newline character at the end of the line by a backslash (\).

macro definitions (#define, #undef)

#define identifier replacement
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE];
 // function macro
#include <iostream>
using namespace std;

#define getmax(a,b) ((a)>(b)?(a):(b))

int main()
{
  int x=5, y;
  y= getmax(x,2);
  cout << y << endl;
  cout << getmax(7,x) << endl;
  return 0;
}
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];

This would generate the same code as:

int table1[100];
int table2[200];

Function macro definitions accept two special operators (# and ##) in the replacement sequence:
The operator #, followed by a parameter name, is replaced by a string literal that contains the argument passed (as if enclosed between double quotes):

#define str(x) #x
cout << str(test);

The operator ## concatenates two arguments leaving no blank spaces between them:

#define glue(a,b) a ## b
glue(c,out) << "test";
#define AA(a,b)#a###b

int main(int argc,char *argv[])
{
    qDebug()<<AA(v1,_m);//v1_m
}

Conditional inclusions (#ifdef, #ifndef, #if, #endif, #else and #elif)

#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif  
#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];
#if TABLE_SIZE>200
#undef TABLE_SIZE
#define TABLE_SIZE 200
 
#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50
 
#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif
 
int table[TABLE_SIZE];
#if defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
#elif !defined BUFFER_SIZE
#define TABLE_SIZE 128
#else
#define TABLE_SIZE BUFFER_SIZE
#endif

Qt Objects

The QObject class is the base class of all Qt objects

Q_OBJECT Macro

The Q_OBJECT Macro is probably one of the weirdest things to whoever beginning to use Qt.

Qt QObject Class says:

The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt’s meta-object system.

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

signals:
    void mySignal();

public slots:
    void mySlot();
};
class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)
    Q_ENUMS(Priority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };

    MyClass(QObject *parent = 0);
    ~MyClass();

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;

So, it sounds like we need it to use signal and slot, and probably for other purposes (meta-object related) as well.

Another doc related to moc explains:

The Meta-Object Compiler, moc, is the program that handles Qt’s C++ extensions.

The moc tool reads a C++ header file. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces a C++ source file containing the meta-object code for those classes. Among other things, meta-object code is required for the signals and slots mechanism, the run-time type information, and the dynamic property system.

moc limitations

Multiple Inheritance Requires QObject to Be First

// correct
class SomeClass : public QObject, public OtherClass
{
    ...
};

Function Pointers Cannot Be Signal or Slot Parameters

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};

// instead you can do next
typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

Nested Classes Cannot Have Signals or Slots

class A
{
public:
    class B
    {
        Q_OBJECT

    public slots:   // WRONG
        void b();
    };
};

Signal/Slot return types cannot be references

Only Signals and Slots May Appear in the signals and slots Sections of a Class

QMetaObject

The QMetaObject class contains meta-information about Qt objects

The Qt Meta-Object System in Qt is responsible for the signals and slots inter-object communication mechanism, runtime type information, and the Qt property system. A single QMetaObject instance is created for each QObject subclass that is used in an application, and this instance stores all the meta-information for the QObject subclass. This object is available as QObject::metaObject().

This class is not normally required for application programming, but it is useful if you write meta-applications, such as scripting engines or GUI builders.

The functions you are most likely to find useful are these:

The index functions indexOfConstructor(), indexOfMethod(), indexOfEnumerator(), and indexOfProperty() map names of constructors, member functions, enumerators, or properties to indexes in the meta-object. For example, Qt uses indexOfMethod() internally when you connect a signal to a slot.

Classes can also have a list of namevalue pairs of additional class information, stored in QMetaClassInfo objects. The number of pairs is returned by classInfoCount(), single pairs are returned by classInfo(), and you can search for pairs with indexOfClassInfo().

Q_INVOKABLE void fun();
emmiter->metaObject()->method(emmiter->metaObject()->indexOfMethod("fun()")).invoke(emmiter,Qt::ConnectionType::DirectConnection,Q_ARG(QObject*, emmiter));

private slots:
    void MySlot(QObject* obj);

QMetaObject::invokeMethod(emmiter,"MySlot",Qt::ConnectionType::DirectConnection,Q_ARG(QObject*, emmiter));
//invoke methods defined with public slots or private slots or Q_INVOKABLE 

class Test:QObject
{
    Q_OBJECT
public:
    enum Myen{A,B,C};
    Q_ENUM(Myen);

    Test();
    Q_INVOKABLE void fun(Test::Myen en);
};
Q_DECLARE_METATYPE(Test::Myen);


bool b=QMetaObject::invokeMethod(this,"fun",Qt::ConnectionType::QueuedConnection,Q_ARG(Test::Myen,A));

//call fun function Test::A

QObject  Structure

QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent’s children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor. You can look for an object by name and optionally type using findChild() or findChildren().

QObject(QObject *parent = nullptr)
parent() const
setParent(QObject *parent)
objectName() const
setObjectName(const QString &name)
findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) 
findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively)
children() const

inherits check

QTimer *timer = new QTimer;         // QTimer inherits QObject
timer->inherits("QTimer");          // returns true
timer->inherits("QObject");         // returns true
timer->inherits("QAbstractButton"); // returns false

// QVBoxLayout inherits QObject and QLayoutItem
QVBoxLayout *layout = new QVBoxLayout;
layout->inherits("QObject");        // returns true
layout->inherits("QLayoutItem");    // returns true (even though QLayoutItem is not a QObject)

QObject *obj = new QTimer;          // QTimer inherits QObject

QTimer *timer = qobject_cast<QTimer *>(obj);
// timer == (QObject *)obj

QAbstractButton *button = qobject_cast<QAbstractButton *>(obj);
// button == nullptr

Dynamic Properties (QMetaProperty)

class MyClass: public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)//registered as QMetaEnum

    void setPriority(Priority priority)// is not diffrent if private or public
    {
        m_priority = priority;
        emit priorityChanged(priority);
    }
    Priority priority() const
    { return m_priority; }

signals:
    void priorityChanged(Priority);

private:
    Priority m_priority;
};

MyClass *myinstance = new MyClass;
QObject *object = myinstance;

myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
property(const char *name) const//call read function or get value of member
bool QObject::setProperty(const char *name, const QVariant &value)//call write function or set value to member
class Test:QObject
{
    Q_OBJECT
public:
    enum Myen{A,B,C};
    Q_ENUM(Myen);

    Test();
    Q_INVOKABLE void fun(Test::Myen en);// once you declare metataype as Test::Myen the argument should to be Test::Myen
};
Q_DECLARE_METATYPE(Test::Myen);

class A{
public:
    operator QString(){
        return QStringLiteral("A");
    }
};
Q_DECLARE_METATYPE(A);
Test::Test()
{
    QString retval;
    bool b=QMetaObject::invokeMethod(this,"fun",Qt::ConnectionType::QueuedConnection,Q_ARG(Test::Myen,A));
    qDebug()<<"end constructor";
}

void Test::fun(Test::Myen en)
{
    qDebug()<<"call fun function"<<en;
}

Signals and Slots

observer design pattern
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
   QPushButton *quitButton = new QPushButton("Quit");
   QObject::connect(quitButton, SIGNAL(clicked()),
           &app, SLOT(quit()));
   quitButton->show();
return app.exec();
}

A signal can also be connected to another signal:

class MyWidget::public QWidget
{
    Q_OBJECT

public:
    MyWidget();

signals:
    void buttonClicked();

private:
    QPushButton *myButton;
};

MyWidget::MyWidget()
{
    myButton = new QPushButton(this);
    connect(myButton, SIGNAL(clicked()),
            this, SIGNAL(buttonClicked()));
}

The signal must be a function declared as a signal in the header. The slot function can be any member function that can be connected to the signal. A slot can be connected to a given signal if the signal has at least as many arguments as the slot, and there is an implicit conversion between the types of the corresponding arguments in the signal and the slot.

QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
                 label,  &QLabel::setText);
connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
all connect functions return QMetaObject::Connection we can use it for disconnect later

enum Qt::ConnectionType

This enum describes the types of connection that can be used between signals and slots. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time.

Constant
Qt::AutoConnection
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Qt::DirectConnection
The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
Qt::QueuedConnection
The slot is invoked when control returns to the event loop of the receiver’s thread. The slot is executed in the receiver’s thread.
Qt::BlockingQueuedConnection
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
Qt::UniqueConnection
This is a flag that can be combined with any one of the above connection types, using a bitwise OR. When Qt::UniqueConnection is set, QObject::connect() will fail if the connection already exists (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6.
#include <QtCore>

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);

signals:
    void MySignal();
public:
    void EmitSignal();
public slots://not necesorry only if you wnat to use it as SLOT(MySlot())
    void MySlot();

};
#include "test.h"

Test::Test(QObject *parent) : QObject(parent)
{

}

void Test::EmitSignal(){
    emit this->MySignal();
    qDebug()<<"signal is emmited";
}

void Test::MySlot(){
    qDebug()<<"slot is called"<<sender();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Test emmiter,reciver;

    QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot,Qt::ConnectionType::QueuedConnection);
    //print signal is emmited then slot is called
    
    QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot,Qt::ConnectionType::DirectConnection);
    //print slot is called then signal is emmited

    emmiter.EmitSignal();
    return a.exec();
}

there are many ways to add signal and slot to connect

QObject::connect(&emmiter,SIGNAL(MySignal()),&reciver,SLOT(MySlot()));//slot here should to be Q_INVOKABLE or public private slots:
QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot)// slot here can be any function member of object

disconnect

disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
disconnect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)
disconnect(const QMetaObject::Connection &connection)
disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)
all of them return bool value

before we were use public static method to connect and disconnect but actually you can use public non-static members

disconnect(const char *signal = nullptr, const QObject *receiver = nullptr, const char *method = nullptr)
connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
disconnect(const QObject *receiver, const char *method = nullptr) const
void blockSignals(bool block) // non-static member to block any signal come from this object
bool signalsBlocked() const// to check if signals comming from this object is blocked or not
int	receivers(const char *signal) const //protected method number of recivers
QObject *	sender() const // protected method can use it inside any slot to get the sender object
int	senderSignalIndex() const //meta-method index of the signal that called the currently executing slot
QCoreApplication a(argc, argv);
Test *emmiter=new Test{} ,*reciver=new Test{};
QObject::connect(emmiter,&Test::destroyed,reciver,&Test::MySlot);
qDebug()<<emmiter;
delete emmiter;
return a.exec();
//===============================
void Test::MySlot(QObject* obj){
    qDebug()<<"slot is called \n"<<obj;
}

QSignalMapper

QGridLayout *gridLayout = new QGridLayout(this);
QSignalMapper *mapper = new QSignalMapper(this);
for(int row = 0; row < 3; ++row) {
    for(int column = 0; column < 3; ++column) {
        QPushButton *button = new QPushButton(" ");
        gridLayout->addWidget(button, row, column);
        m_board.append(button);
        mapper->setMapping(button, m_board.count() - 1);
        connect(button, SIGNAL(clicked()), mapper, SLOT(map()));
    }
}
connect(mapper, SIGNAL(mapped(int)),
        this,   SLOT(handleButtonClick(int)));

QObject predefined signals and slots

deleteLater()
destroyed(QObject *obj = nullptr)
objectNameChanged(const QString &objectName)

Events System

Qt creates an event object to represent it by constructing an instance of the appropriate QEvent subclass, and delivers it to a particular instance of QObject (or one of its subclasses) by calling its event() function.
This function does not handle the event itself; based on the type of event delivered, it calls an event handler for that specific type of event, and sends a response based on whether the event was accepted or ignored.

bool MyWidget::event(QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab) {
            // special tab handling here
            return true;
        }
    } else if (event->type() == MyCustomEventType) {
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return true;
    }

    return QWidget::event(event);
}

Note that event() is still called for all of the cases not handled, and that the return value indicates whether an event was dealt with; a true value prevents the event from being sent on to other objects.

protected:
    bool event(QEvent *event) override{
        qDebug()<<"call event \n";
        QObject::event(event);//call timerEvent
        return false;
    }

    void timerEvent(QTimerEvent *event) override{
        qDebug()<<"call timer event \n";
    }

    bool eventFilter(QObject *watched, QEvent *event) override{
        //watched = pointer to object carch event
        qDebug()<<"call event filter \n";
        return false; // false meen can call event function
    }


    Test t;
    t.startTimer(1000);
    t.installEventFilter(&t);
call event filter
call event
call timer event

When the filter object’s eventFilter() implementation is called, it can accept or reject the event, and allow or deny further processing of the event. If all the event filters allow further processing of an event (by each returning false), the event is sent to the target object itself. If one of them stops processing (by returning true), the target and any later event filters do not get to see the event at all.

Sending Events

Many applications want to create and send their own events. You can send events in exactly the same ways as Qt’s own event loop by constructing suitable event objects and sending them with QCoreApplication::sendEvent() and QCoreApplication::postEvent().

sendEvent() processes the event immediately. When it returns, the event filters and/or the object itself have already processed the event. For many event classes there is a function called isAccepted() that tells you whether the event was accepted or rejected by the last handler that was called.

postEvent() posts the event on a queue for later dispatch. The next time Qt’s main event loop runs, it dispatches all posted events, with some optimization. For example, if there are several resize events, they are compressed into one. The same applies to paint events: QWidget::update() calls postEvent(), which eliminates flickering and increases speed by avoiding multiple repaints.

postEvent() is also used during object initialization, since the posted event will typically be dispatched very soon after the initialization of the object is complete. When implementing a widget, it is important to realize that events can be delivered very early in its lifetime so, in its constructor, be sure to initialize member variables early on, before there’s any chance that it might receive an event.

To create events of a custom type, you need to define an event number, which must be greater than QEvent::User, and you may need to subclass QEvent in order to pass specific information about your custom event. See the QEvent documentation for further details.

Q_GADGET

The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes that do not inherit from QObject but still want to use some of the reflection capabilities offered by QMetaObject. Just like the Q_OBJECT macro, it must appear in the private section of a class definition.

Q_GADGETs can have Q_ENUMQ_PROPERTY and Q_INVOKABLE, but they cannot have signals or slots.

Q_GADGET makes a class member, staticMetaObject, available. staticMetaObject is of type QMetaObject and provides access to the enums declared with Q_ENUMS.

links
https://doc.qt.io/qt-5/qobject.html
https://doc.qt.io/qt-5/qmetaobject.html