6 default functions and operator overloading of C++

C++ classes and objects

We know that C language is a process-oriented programming language, and C++ is an object-oriented (OPP) programming language. 
Object-oriented is a programming paradigm and a method of program development. The object refers to the instance of the class, and the object is used as the basic unit of the program, and the program and data are encapsulated in it to improve the importance, flexibility and scalability of the software.  
Class (class) is unique in C++, similar to the structure in C language! 
Classes have three major characteristics: encapsulation, inheritance, and polymorphism 
. In a class, there are two types of objects. They are member variables (data) and member functions (functions).

Encapsulation (encapsulation) Encapsulation is to combine the abstracted data and behavior (or function) to form an organic whole, that is, to organically combine the data with the source code for operating the data. 
The purpose of encapsulation is to enhance security and simplify programming. Users do not need to know the specific implementation details, but only use the members of the class through the external interface and a specific access right.

Polymorphism is a technology that allows you to set a parent object to be equal to one or more of its child objects. After assignment, the >>>parent object can operate in different ways according to the characteristics of the child object currently assigned to it<<< (from "Delphi4 Programming Technology Insider"). To put it simply, it is a sentence: it is allowed to assign a pointer of a subclass type to a pointer of a parent class type.

"Inheritance" is a concept in object-oriented software technology. If a class A inherits from another class B, this A is called a "subclass of B", and B is called a "parent class of A". Inheritance allows subclasses to have various properties and methods of the parent class without having to write the same code again.

There are three access qualifiers in the class: 1. public (public) 2. protectd (protected) 3. private (private)

1. Public members can be directly accessed from outside the class, and private/protected members cannot be directly accessed from outside the class. 
2. Each qualifier can be used multiple times in the class body, and its scope is from the appearance of the qualifier to before the next qualifier or before the end of the class body. 
3. If no qualifier is defined in the class body, it will be private by default. 
4. The class access qualifier embodies object-oriented encapsulation.

Class scope:

1. Each class defines its own scope, and the members (member functions/member variables) of the class are all within the scope of the class, and member variables and other member functions can be accessed arbitrarily within the member function. 
2. The object can directly access the public members through ., and the pointer to the object can also directly access the public members of the object through ->. 
3. To define members outside the class, you need to use the : : scope parser to indicate which class domain the member belongs to.

One or more functions can be written in a class, and the scope of this function can be the class domain or the global domain. Functions can be written in tears or outside the class, but they must be declared in the class and the scope parser must be added.

class AA//AA为类名
{
pubilc://公有访问限定符
void Display()//Display就是成员函数
{
cout<<"Display()"<<endl;
}
private://私有访问限定符
int year;//这些为成员变量
int month;
int day;
};

This function is a member function defined inside the class, or it can be defined outside the class

class AA
{
public:
void Display();
private:
int year;
int month;
int day;
};

void AA::Display() //表明Display函数属于AA类
{
cout<<"Dispaly()"<<endl;
}
class instance object

1. A class is just a model, which defines which members the class has, and defines a class without allocating actual memory space to store it. 
2. A class can instantiate multiple objects, and the instantiated objects occupy actual physical space to store class member variables. 
3. Make an analogy. Instantiating an object from a class is like using architectural design drawings to build a house in reality. A class is like a design drawing. You only design what you need, but there is no physical building. The same class is just a design. The instantiated object can actually store data and occupy physical space 
.

int main()
{
AA d1;
d1.Display();
return 0;
}

Here d1 is the object instantiated by AA. 
The calculated size in a structure is the sum of the sizes of all members. The class size is also the sum of the size of the member variables.

Structure memory alignment rules:

1 . 第一个成员在与结构体变量偏移量为0 的地址处。 
2 . 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 
//对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 
V S中默认的值为8 
gc c中的默认值为4 
3 . 结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。 
4 . 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体 
的对齐数)的整数倍。

class BB
{
char ch;//0 0-7
double d;//8 8 8 8-15
};

这个简单的类,他的大小就是char 和 double的大小,但要满足对齐规则。所以大小就应该是16。

class BB
{
char ch;//0 0-7
double d;//8 8 8 8-15
};
class CC
{
public:
char a;//0 8 0
BB b; // 16 8 8-23
char c;//24 25-31
};

这种涉及到嵌套的类求大小也满足对齐规则。

Test()
{
cout<<sizeof(CC)<<endl;
}


在类中有隐含的this指针

1 . 每个成员函数都有一个指针形参,它的名字是固定的,称为t his 指针,t his 指针是隐式的。(构造函数比较特殊,没有这个隐含t his 形参) 
2 . 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参t his 指针。 
3 . t his 指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加t his 指针的参数定义,也不能在调用时显示传递对象的地址给t his 指针。

void Display()
{
cout<<year<<"-"<<month<<"-"<<day<<endl;
}
//上面类中的函数就相当于
void Display(AA *this)
{
cout<<*this->year<<"-"<<*this->month<<"-"<<*this->day<<endl;
}

this指针并不是用户在编写代码时所要写的,而是编译器在处理成员函数时所要做的工作。

类的六个默认成员函数

1、构造函数 
2、拷贝构造函数 
3、析构函数 
4、赋值操作符的重载 
5、取地址操作符的重载 
6、const修饰的取地址操作符的重载

前四个一般在实际中应用最多,所以只看前四个 
先来看看构造函数

构造函数

在类中,私有的成员变量在类外是访问不到的那么要怎么对他进行初始化呢?这时候就要有一个共有函数来对他进项初始化,同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数。

构造函数有以下特征

1 . 函数名与类名相同。 
2 . 无返回值。 
3 . 对象构造(对象实例化)时系统自动调用对应的构造函数。 
4 . 构造函数可以重载。 
5 . 构造函数可以在类中定义,也可以在类外定义。 
6 . 如果类定义中没有给出构造函数,则C + + 编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动 
生成缺省的构造函数。 
7 . 无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。

class AA
{
public:
void Display();
private:
int year;
int month;
int day;
};

void AA::Display()
{
cout<<year<<month<<day<<endl;
}

这个类在调用Display函数时,会将类里面的私有成员变量的值打印输出。 
这里会输出什么值呢?


输出的是随机值,这其实就是调用了一次构造函数,只不过这个构造函数是系统自动生成的,没有对成员变量进行初始化。可以用户自己定义一个构造函数

class AA
{
public:
void Display();
AA(int _year, int _month, int _day)
{
year = _year;
month = _month;
day = _day;
}
/*
AA(int _year = 1900, int _month = 1, int _day = 1)
{
year = _year;
month = _month;
day = _day;
}
*/
private:
int year;
int month;
int day;
};
Test()
{
AA a1;
AA a2(2017,7,1);
}

这种是由用户自定义的构造函数,可以是无参的构造,也可以是带参数的构造函数,也可以是有缺省和半缺省的构造函数,具体实现要看具体过程。

拷贝构造函数

创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造(CopyConstructor ),拷贝构造函数是特殊的构造函数。 
拷贝构造函数有以下特征:

1 . 拷贝构造函数其实是一个构造函数的重载。 
2 . 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。(思考为什么?) 
3 . 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。

AA (const AA& d)
{
year = d.year;//year是私有变量成员,可以在类中用拷贝构造将私有成员拷贝到公有成员函数里,可以达到访问私有变量的目的
month = d.month;
day = d.day;
}

在调用拷贝构造函数时

test()
{
AA a1;
AA a2(a1);
AA a3 = a1; //这也可以表示成拷贝构造
}

析构函数

当一个对象的生命周期结束时,C + + 编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor ) 
构造函数是特殊的成员函数,其特征如下:

1 . 析构函数在类名加上字符~ 。 
2 . 析构函数无参数无返回值。 
3 . 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。 
4 . 对象生命周期结束时,C + + 编译系统系统自动调用析构函数。 
5 . 注意析构函数体内并不是删除对象,而是做一些清理工作。(怎么理解这里的清理工作?参看下面的EXP0 )

~AA()//析构函数在程序结束时会自动调用
{
cout<<"~AA()"<<endl;
}

系统调用析构函数是判断生成的构造函数和拷贝构造的次数来决定调用几次析构函数。

运算符重载

为了增强程序的可读性,C + + 支持运算符重载。 
运算符重载特征:

1 . operator + 合法的运算符 构成函数名(重载< 运算符的函数名:operator < )。 
2 . 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。

AA& operator=(const AA& d)//将=运算符进行重载,这里用引用传参和引用返回能够更加快速的调用,同时减少不用再压栈
{
if(this != &d)
{
year = d.year;
month = d.month;
day = d.day;
}
return *this;
}
Test()
{
AA d1(1900,1,1);
AA d2;
d2 = d1;
}

有以下几个运算符不支持重载 
. * /: : /s izeof/?: /.

C++中隐含的this指针

先来看看什么this指针

1 . 每个成员函数都有一个指针形参,它的名字是固定的,称为t his 指针,t his 指针是隐式的。(构造函数比较特殊,没有这个隐含t his 形 
参) 
2 . 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参t his 指针。 
3 . t his 指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加t his 指针的参数定义,也不能在调用时 
显示传递对象的地址给t his 指针

在刚刚上面的运算符重载问题里我们只是传了一个const AA& d,而实际上是什么样的呢?编译器在处理的时候是怎么将这个赋值运算符重载进行编译的。

    AA& operator=(AA *thisconst AA& d)
{
if(this != &d)
{
year = d.year;
month = d.month;
day = d.day;
}
return *this;
}


重载函数在接收到的第一个参数相当于是接收到d1的地址,然后通过this指针来隐藏,而编译器在处理的时候会把this展开成d1。所以在cpp类中一般都会有默认的this指针来接收。

Guess you like

Origin blog.csdn.net/prokgtfy9n18/article/details/74358879