C++ learning summary 2

class MyObj
{
public:
MyObj(int x) : x_(x){}
void setValue(int x) { x_ = x;}
void show(void)
{
cout<<x_<<endl;
}
private:
int x_;
};

MyObj fun(void)
{ return MyObj(4); //Generate an object directly and return; }

int main()
{
MyObj obj = fun();
obj.show();
return 0;
}

//In object-oriented programming, simple usage of combination:
#include
#include

using namespace std;

class A
{
public:
A(int x) : x_(x){cout<<“A constructor”<<endl;}
private:
int x_;
};

class B
{
public:
B(int y,int xx) : y_(y),a1(xx){cout<<“B constructor”<<endl;}
private:
int y_;
A a1;
};

class C
{
public:
C(int y,int xx) : y_(y),a1(xx),b1(4,6){cout<<“C constructor”<<endl;}
private:
int y_;
A a1;
B b1;
};

int main()
{
C c(22,33);
return 0;
}
// output message
// A constructor
// A constructor
// B constructor
// C constructor

3) Application cases of delegation relationship
#include
#include

using namespace std;

//Delegation: You can separate the interface and implementation, and switch different implementation versions according to different needs;
class A; //Declare class A;
class B
{ friend class A; public: B(int y) : y_(y){cout< <“B constructor”<<endl;} A* aptr; private: int y_; };






class A
{
public:
A(int x) : x_(x){cout<<“A constructor”<<endl;}
void show(void){cout<<“a class handle—”<<endl;}
private:
int x_;
};

int main()
{
A a(88);
B bb(76);
bb.aptr->show();
return 0;
}

4) Conversion function operator type(), application case:
//operator type() general form of conversion function
class Fraction { public: Fraction(int num, int den) : m_numerator(num), m_denominator(den) {}


operator double() const //转换函数的写法,不是必须基本类型,自定义类型也可以,只要认为可以转换即可
{
    return (double) (static_cast<float>(m_numerator) / static_cast<float>(m_denominator));
}

private:
int m_numerator; // numerator
int m_denominator; // denominator
};

Fraction f(3, 5);
double d = 4 + f; // Call operator double to convert f to double

5)non-explicit one-argument constructor:(explicit在构造函数中的用法)
class Fraction
{
public:
explicit Fraction(int num,int den = 1) : m_numerator(num),m_denominator(den){}
Fraction operator+(const Fraction& f)
{
Fraction tmp(0,0);
tmp.m_numerator = this->m_numerator + f.m_numerator;
tmp.m_denominator = this->m_denominator + f.m_denominator;
return tmp;
}
private:
int m_numerator;
int m_denominator;
};

int main()
{ Fraction f(3,5); Fraction d = f + 4; //If the constructor adds explicit, this sentence is wrong and it is forbidden to convert 4 into a Fraction object; return 0; }



6) Internal code analysis of smart pointers
//The generated objects are like pointers, and the generated objects are like functions;
//That is, smart pointers and functors;

template
class Myshared_ptr
{
public:
T& operator*() const
{ return px; }
T
operator->() const
{ return px;}
Myshared_ptr(T* p) : px§{}
private:
T* px;
};
struct Foo
{
void method(void)
{
cout<<“method handle”<<endl;
}
};

int main()
{ Myshared_ptr sp(new Foo); sp->method();//Equivalent to px->method(); return 0; }



7) Use case of functor:
class A
{ public: int operator()(int x) { return (3 + x); } };





int main()
{ A a; cout<<a(6)<<endl; cout<<A()(8)<<endl; //Note: A() is a temporary object, and operator overloading will be called later; return 0; } //The output is 9 and 11;





8) Usage of namespace
namespace jj01
{ void printMsg1(void) { cout<<“jj01-------”<<endl; } } //Note that there is no semicolon here;




namespace jj02
{
void printMsg1(void)
{
cout<<“jj02---------”<<endl;
}
}
int main()
{
jj01::printMsg1();
jj02::printMsg1();
return 0;
}

9) Use cases and significance of template specialization and partial specialization:
//Generalized
template
struct myhash{ void show(){cout<<“general --”<<endl;}};

//Specialization (it is more accurate to call full specialization here)
//The meaning of display specialization (full specialization): When the template parameters require special processing, specialization needs to be displayed
//The meaning of partial specialization: When the template When the parameters meet some conditions, processing is performed;
template<>
struct myhash
{ size_t operator()(char x) const { cout<<x<<endl; return 0; } };





int main()
{
myhash()(‘u’);
return 0;
}

9-2) Usage scenarios of partial specialization
//The following application scenarios of partial specialization are: when a pointer is passed in, partial specialization will be selected;
//Generalized
template
struct Foo
{ public: void show(void ) { cout<<i<<endl; } private: const int i = 33; };







//偏特化
template
struct Foo<T*>
{
public:
void show(void)
{
cout<<i<<endl;
}
private:
const int i = 2;
};

int main()
{ Foo<int*> f1; f1.show(); //The partially specialized template class Foo f2; f2.show(); //The generalized template class is called return 0; }





10) Variable parameter template
//Syntax of variable parameter template
//The following two sentences mean to tell the compiler that the number of my template parameters is variable,
//The number of my function parameters is also variable ;
template<typename… Args>
void fun(Args… args)
{

}

int main()
{
fun(1,2,“hello”);
fun(1,4.6);
fun(33);
return 0;
}

//Classic variable template parameter case:
void fun(void)
{ cout<<“no paramter”<<endl; }

template<typename T, typename… Args>
void fun(const T& firstArg,Args… args)
{
cout<<firstArg<<endl;
fun(args…);
}

int main()
{
fun(34,“hhlo”,23.67);
return 0;
}

11) The difference between pointers and references
int x = 0;
int* p = &x; //p points to x;
int& r = x; //r represents x;
reference is the relationship represented;

The object and reference have the same size and the same address (all are illusions)
int main(void)
{ int x = 0; int& r = x; //r represents x; cout<<sizeof(x)<<endl; //4 cout<<sizeof®<<endl; //4, but in fact the underlying logic is a pointer, so it is 8 in 64bit; cout<<&x<<endl; //0x7ffef291eb0c cout<<&r<<endl; //0x7ffef291eb0c return 0; }







12) Polymorphic application case:
//The object is a pig, and the pointer is an animal when declared, (upward transformation)
class Shape
{ public: virtual void draw(void) = 0; };


class Circle : public Shape
{
public:
virtual void draw(void)
{
cout<<“draw circle—”<<endl;
}
};

class Square : public Shape
{
public:
virtual void draw(void)
{
cout<<“draw square—”<<endl;
}
};

void fun(Shape* obj)
{
obj->draw();
}

int main(void)
{
Circle c1;
Square s1;
fun(&c1);
fun(&s1);
return 0;
}

13) Member functions with const and member functions without const have different member function signatures;
when a member function has a const version and a non-const version, the
const object calls the member function with the const version, and
the non-const object calls the member function. is a non-const version of the member function;
class Test
{ public: void show(void){cout<<“hello world”<<endl;} void show(void)const {cout<<“const hello world”<<endl; } };



int main(void)
{
Test t1;
t1.show();

const Test t2;
t2.show();
return 0;

}

14) Memory layout of C++ objects: factors that affect the size of C++ objects.
Static member variables and functions are independent of a single instantiated object. Among them, functions include member functions, virtual functions, static member functions, etc.;
three factors that affect the size of C++ objects: Non-static data members, virtual functions and byte alignment;
//virtual table pointer and type_info
class A
{ public: A(){} virtual ~A(){} // virtual void show(void){} void show() {} static void fun(){} };






int main(void)
{
cout<<sizeof(A)<<endl;
cout<<sizeof(float)<<endl;
return 0;
}

C++ stipulates that the size of empty class objects is at least 1 byte, just to distinguish instantiated objects;
for example, multiple empty class objects can be created and can be distinguished by the memory address of the object;

The memory layout and address growth of non-static data members are consistent;

15) Design Patterns
// 23 design patterns (clever way to remember: homophones or homophones are used)
// Create a park, but worry // 5 creative patterns: factory mode, prototype mode, singleton mode, builder Mode, abstract factory mode;
//Sister wants to rent out, install bridge from generation to generation //7 structural modes: flyweight mode, appearance mode, combination mode, adapter mode, proxy mode, decorator mode, bridge mode; //
Shape Compromise imitation, alert observation whistle // 11 behavioral modes: status mode, chain of responsibility mode, intermediary mode, template mode, visitor mode; interpreter mode, memo mode, observer mode, strategy mode, command mode, iterator pattern;

It is very important to maintain the integrity of the class;
① If a member object in a class has a nontrivial default constructor, the compiler will generate a nontrivial default constructor for our class.
So why does the compiler do this?
The answer is because the class member object has a nontrivial default constructor, so the compiler needs to explicitly call the nontrivial default constructor of the class member object. If the compiler wants to explicitly call the nontrivial default constructor of a class member object, it needs to synthesize some code to call it. But remember, the nontrivial default constructor synthesized by the compiler only calls the default constructor of the class member object and does not perform any initialization operations on other variables in our class.
That is to say, if you want to initialize variables other than class member variables, such as an int or a String, you must define a default constructor yourself to complete the initialization of these variables. The compiler will expand the default constructor you defined accordingly, thereby calling the nontrivial default constructor of the class member object.
② If the base class of a derived class has a nontrivial default constructor, the compiler will synthesize a nontrivial default constructor for the derived class.
The reason why the compiler does this is because when a derived class is synthesized, it needs to explicitly call the default constructor of the base class.
③ How can a class implicitly contain any virtual function table (or vtbl), pointer member (or vptr).
The reason why the compiler does this is simple: because these vtbl or vptr need to be synthesized implicitly by the compiler, then the compiler puts the synthesis action in the default constructor. So the compiler must generate a default constructor to complete these operations.
So if your class has any virtual function, the compiler will synthesize a default constructor for you.
④If a class virtually inherits from other classes.
The reason why the compiler does this is similar to ③: because virtual inheritance needs to maintain something like a pointer that can dynamically determine the memory address (different compilers implement virtual inheritance not only the same).
So except for the above four cases, the compiler will not generate a default constructor for our class.
Therefore, do not take things for granted when programming. You must understand which things are done by the compiler and which things need to be completed by programmers. Just like the resources occupied by the heap need to be released by the programmer, the stack space is managed by the compiler.
Only in this way can you write higher quality code.

2023-7-24
1) The member functions of a class do not occupy the memory space of the class object.
2) The member functions do not occupy the object space. They follow the class. Only one is born for each class.
3) Static member variables are stored outside the object. , indicating that the occupied memory space has nothing to do with the object;
4) As long as there is at least one virtual function in the class, a virtual table pointer will be generated, and this pointer just points to the virtual function table;
5) If there are multiple virtual functions in the class, There will be multiple virtual function pointers, and these pointers need to be placed somewhere, so they will be put into a table. This table is called a virtual function table; this
virtual function table is compiled into the executable file and is used when the program is executed. It will be loaded into the memory when the function table is loaded into the memory;
the virtual function table is based on the class and follows the class; (Note: In the class object, because of the existence of virtual functions, a virtual table pointer will be added to the class object, not a Virtual function table!!!);
Summary: The virtual function does not calculate the sizeof() of the class object. The virtual function table is based on the class, and the virtual table pointer is based on the class object! ! !

6) This pointer adjustment: Multiple inheritance and inheritance order determine the later memory layout;
to put it bluntly, when calling a member function of object A, the this pointer will be adjusted to point to the member function of object A; when calling object B member function, the this pointer will be adjusted to point to the member function of the B object;
class A
{ public: A() { printf("The this pointer of A::A() is: %p\r\n", this); } void funcA() { printf(“The this pointer of A::funcA() is: %p\r\n”,this); } int a; };










class B
{ public: B() { printf(“The this pointer of B::B() is: %p\r\n”,this); } void funcB() { printf(“B::funcB() The this pointer is: %p\r\n”,this); } int b; };










class C : public B,public A
{ public: C() { printf(“The this pointer of C::C() is: %p\r\n”,this); } void funcC() { printf(“C The this pointer of ::funcC() is: %p\r\n”,this); } int c; };










//Synthetic constructor condition 1:
This class has no constructor, but the class contains a program with an object type. This object member has a constructor. At this time, the compiler will synthesize a constructor. The purpose of synthesis is to call the object member constructor;

Program conversion semantics:
1) Initialize the object when defining:
X x3 = (x0);
the compiler will split the above statement into two lines, the code is similar to the
following
: :X(x0); Then call the copy constructor;

2) Initialization of parameters
class CValue
{ public: int val1; int val2; public: CValue(int v1 = 0,int v2 = 0) : val1(v1),val2(v2){cout<<"Constructor called" <<endl;} CValue(const CValue& obj): val1(obj.val1),val2(obj.val2) //Note that the copy constructor also uses an initialization list { cout<<“The copy constructor was called”<<endl ; } ~CValue(){cout<<"Destructor called-----"<<endl;} };










CValue func(CValue& obj) //If you don’t write the copy constructor, this function will be compiled incorrectly; 2) It is too important to use references, otherwise there will be one more constructor { // CValue tmp
; // tmp.val1 = obj.val1 * 2; // tmp.val2 = obj.val2 * 2; // return tmp; return CValue(obj.val1 * 2,obj.val2 * 2); //The above four sentences can be improved to this statement. If you write The compiler will also optimize the above four statements into this statement }





int main()
{
CValue value(2,4);
auto e = func(value);
return 0;
}

2) Type conversion constructor
class X
{ public: explicit X(int x) : x_(x){cout<<"X constructor execution"<<endl;} public: int x_; };




int main()
{ X x1(20); // X x2 = 10; //Type conversion constructor, (involving implicit type conversion) return 0; }



3) Go deep into bitwise copy of C++;
if there are only some simple member variable types, such as int double, then there is no need to copy the constructor at all. The compiler internally supports bitwise (bitwise) copy of member variables; if you
add If you add your own copy constructor, the compiler's bitwise copying capability will be invalid. Therefore, if you add a copy constructor, you will be responsible for the initialization yourself; class X { public: explicit
X
( int x, float mm) : x_( x),mm_(mm) {cout<<"X constructor execution"<<endl;} // If you add the following copy constructor, bitwise (bitwise copy) will be invalid; // X(const X& obj) : x_(obj.x_),mm_(obj.mm_){cout<<"X copy constructor execution"<<endl; } public: int x_; float mm_; char a; };








int main()
{
X x1(20,67.23);
x1.x_ = 10;
x1.mm_ = 90.8;

    X x2(x1);
    cout<<x2.x_<<endl;
    cout<<x2.mm_<<endl;
    return 0;

}

4) When a pure class is impure, virtual function calling problems occur:
memset(this,0,sizeof(X)) can be used instead of the constructor, and memcpy(this,&tm,sizeof(X)) can be used instead of the copy constructor; class
X
{ public: int x_; int y_ ; int z_ ; X's constructor is executed"<<endl; } X ( const X& obj) { memcpy(this,&obj,sizeof(X)); cout<<" void) { cout<<“Fun function execution”<<endl; } virtual ~X() { cout<<“Virtual analysis function execution----”<<endl; } };






















int main (
) {

    //  X* px0 = new X();  //因为在构造函数中memset的存在,会导致下面语句调用段错误,因为有指针,会有多态发生;
    //  px0->virfun();//通过虚函数指针,找虚函数表,然后通过虚表找到虚函数的地址并调用,但由于虚表指针被清空,所以会报错;
    //  delete px0;

    return 0;

}

If there is no inheritance relationship, the teacher believes that there is no practical difference between virtual functions and ordinary functions;
class X
{ public: int x_ ; int y_; int z_ ; This statement will cause the new object to fail to call the function; cout<<"X's constructor execution"<<endl; } X(const X& obj) { memcpy(this,&obj,sizeof(X)); cout << " The construction constructor of _ _ _ <endl; } virtual ~X() { cout<<"Virtual analysis function execution----"<<endl; } };


























int main()
{
X x0;
x0.virfun();

    X& x1 = x0;
    x1.virfun();

    X* px0 = new X();
    px0->virfun();
    return 0;

}

  1. Static type and dynamic type
    Static type is a defined type;
    dynamic type is the type that the object currently points to (the type is determined at runtime). Only pointers and references have dynamic types;
    and the parent class pointer points to the subclass object;
    A variable may have a static type, a dynamic type, or no dynamic type;

6) Static binding and dynamic binding
Static binding: What is bound is a static type, and the corresponding function or attribute depends on the static type of the object, which occurs at compile time; Dynamic binding: What is
bound is a dynamic type, so the The corresponding function or attribute depends on the dynamic type of the object, which occurs during runtime;
a) Ordinary member functions are statically bound, and virtual functions are dynamically bound;
b) Default parameters are generally statically bound;
Conclusion: It should not Define an inherited non-virtual function in a subclass;
Conclusion 2: Do not redefine the default parameter values ​​of virtual functions;

7) Reflection of polymorphism
From the perspective of code implementation: if you use the virtual function table, it is polymorphic, otherwise it is not;
from the perspective of expression: there is an inheritance relationship, and the parent class must have virtual functions (that is to say, there must be virtual functions in the subclass) function), the derived class overrides the parent class virtual function;

Guess you like

Origin blog.csdn.net/qq_30143193/article/details/131931445