C++ 2|Class declaration and function

2. Class declaration and function

In my opinion,kindcompared tostructurelikestructurecompared toarray, the operability and depth of logical function realization have been improved to a higher level. In C++, the use of classes is somewhat similar to that of structures. The following is the introduction of C++ classes with structures.

Example 7-1, appending data to the end of the array

arr.h

#ifndef _ARR_
#define _ARR_

typedef struct arr{
    
    
	int data[100];
	int tail;
}ARR;

void init(ARR *arr);
void addtail(ARR *arr, int data);
void show(ARR *arr);

#endif

arr.c

#include "arr.h"
#include <stdio.h>

void init(ARR *arr)
{
    
    
	arr->tail = 0;
}

void addtail(ARR *arr, int data)
{
    
    
	arr->data[arr->tail++] = data;
}

void show(ARR *arr)
{
    
    
	int i = 0;
	for(;i<arr->tail; i++)
		printf("%d, ", arr->data[i]);
	printf("\n");
}

main.c

#include "arr.h"

int main()
{
    
    
	ARR arr;
	init(&arr);

	int n = 10;
	while(n--)
		addtail(&arr, n);

	show(&arr);

	int i = 0;
	for(;i<10; i++)
		addtail(&arr, i);

	show(&arr);
}

result:

@ubuntu:/mnt/hgfs/ub2/ARR1$ ls
arr.c  arr.h  main.c
@ubuntu:/mnt/hgfs/ub2/ARR1$ gcc *.c
@ubuntu:/mnt/hgfs/ub2/ARR1$ ./a.out 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
@ubuntu:/mnt/hgfs/ub2/ARR1$ 

Example 7-2. Append data to the end of the array (optimized version)

In Example 7-1, void addtail(ARR *arr, int data); and void show(ARR *arr); are not restricted and can be accessed directly from the outside. This creates a risk of data being inadvertently tampered with. In other words, it is hoped that these functions are bound together and can only be used after initialization to improve code security. Many high-level official codes look like this.
insert image description here

Friends who have used the code generated by STM32CubeMX to play with stm32 microcontrollers will definitely be familiar with this. From another perspective, you must face these when doing embedded microcontroller development.

arr.h

#ifndef _ARR_
#define _ARR_

typedef struct arr{
    
    
	int data[100];
	int tail;

	void (*addtail)(struct arr *arr, int data);
	void (*show)(struct arr *arr);
}ARR;

void init(struct arr *arr);

#endif

arr.c

#include "arr.h"
#include <stdio.h>


static void addtail(ARR *arr, int data)
{
    
    
	arr->data[arr->tail++] = data;
}

static void show(ARR *arr)
{
    
    
	int i = 0;
	for(;i<arr->tail; i++)
		printf("%d, ", arr->data[i]);
	printf("\n");
}

void init(ARR *arr)
{
    
    
	arr->tail = 0;

	arr->addtail = addtail;
	arr->show = show;
}

main.c

#include "arr.h"

int main()
{
    
    
	ARR arr;
	init(&arr);

	int n = 10;
	while(n--)
		arr.addtail(&arr, n);

	arr.show(&arr);
	int i = 0;
	for(;i<10; i++)
		arr.addtail(&arr, i);
    
	arr.show(&arr);

}

result:

@ubuntu:/mnt/hgfs/ub2/ARR2$ ls
arr.c  arr.h  main.c
@ubuntu:/mnt/hgfs/ub2/ARR2$ gcc *.c
@ubuntu:/mnt/hgfs/ub2/ARR2$ ./a.out 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
@ubuntu:/mnt/hgfs/ub2/ARR2$ 

The structure of Example 7-2 above uses a function pointer, making the pointed function a subordinate of the structure (when called, it must be written as arr.addtail() and arr.show()), which fully demonstrates the idea of ​​encapsulation and Object-oriented concept. It also reflects the idea of ​​callbacks (I believe that friends who understand stm32 development should have a deeper understanding of callback functions here).

typedef struct arr{
    
    
	int data[100];
	int tail;
	//两个函数指针
	void (*addtail)(struct arr *arr, int data);
	void (*show)(struct arr *arr);
}ARR;

1. Class declaration

Example 7-3. Append data to the end of the array (C++ version)

arr.h

#ifndef _ARR_
#define _ARR_


class ARR{
    
    
public:

	void addtail(int data);
	void show(void);
	
	
private:
	int data[100];
	int tail;
};


#endif

arr.cpp

#include "arr.h"
#include <stdio.h>


void ARR::addtail(int data)
{
    
    
	this->data[tail++] = data;
    //this关键字标识的是类ARR里的成员(int data[100];的data),与所传参数名(void ARR::addtail(int data)的data)区分开来
}

void ARR::show(void)
{
    
    
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

main.cpp

#include "arr.h"

int main()
{
    
    

	ARR arr;

	int n = 10;
	while(n--)
		arr.addtail(n);

	arr.show();
	int i = 0;
	for(;i<10; i++)
		arr.addtail(i);
    arr.show();
    
}

result:

@ubuntu:/mnt/hgfs/ub2/ARR3$ ls
arr.cpp  arr.h  main.cpp
@ubuntu:/mnt/hgfs/ub2/ARR3$ g++ *.cpp
@ubuntu:/mnt/hgfs/ub2/ARR3$ ./a.out 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
@ubuntu:/mnt/hgfs/ub2/ARR3$ 

2. Member functions of the class

  1. ConstructorA()
  2. Destructor ~A()
  3. Ordinary member function

Example 8-1, the existence of implicit construction -> how many objects are created by judging by the destructor

First declare class A , and then debug by printing destructor information.

#include <stdio.h>

class A{
public:
	~A(){
		printf("A~~~~~~~~~~~~~\n");
	}
};

int main()
{
	A  x ;
	A  y = x;
}

result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_1.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
@ubuntu:/mnt/hgfs/ub2/class$ 

It is not difficult to find out from the results: the destructor is executed twice, so it is concluded that 2 objects have been created .

It can be determined that the constructor can be executed by default, so, is the constructor executed twice? You can write the content of the constructor to test it.

#include <stdio.h>

class A{
public:
	A(){
		printf("A\n");
	}
	~A(){
		printf("A~~~~~~~~~~~~~\n");
	}
};

int main()
{
	A  x ;
	A  y = x;
}

result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_2.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
@ubuntu:/mnt/hgfs/ub2/class$ 

From the results, it can be found that there is only one constructor. Is there one missing? Not really . It's not that there is a missing constructor, but that the constructor isimplicit construction, is the case of A y = x execution, here is usedcopy constructor. The copy constructor executes the default creation object of the system, while ignoring the custom no-argument constructor content printf(“A\n”)

The above is a parameterless constructor. But in many cases, functions need to pass parameters, so you need to use parameterized constructors.

Example 8-2. Constructor with parameters

When using a parameterized constructor, if there is still a default parameterless constructor, you need to write a parameterless constructor.

#include <stdio.h>

class A{
public:
	A(){
		printf("A\n");
	}
	A(int data)
	{
		printf("A(int data)\n");
	}
	~A(){
		printf("A~~~~~~~~~~~~~\n");
	}
};

int main()
{
	A  x = 10; 
	A  y;
}

result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_3.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A(int data)
A
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
@ubuntu:/mnt/hgfs/ub2/class$ 

Here A x = 10; and A x(10); both mean passing parameters.

If you keep the default and don’t write it out, an error will be reported, as follows:

#include <stdio.h>

class A{
public:
	A(int data)
	{
		printf("A(int data)\n");
	}
	~A(){
		printf("A~~~~~~~~~~~~~\n");
	}
};

int main()
{
	A  x = 10;
	A  y;
}

Error result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_4.cpp 
class_construct.cpp: In function ‘int main()’:
class_construct.cpp:17:5: error: no matching function for call to ‘A::A()
  A  y;
     ^
class_construct.cpp:5:2: note: candidate: A::A(int)
  A(int data)
  ^
class_construct.cpp:5:2: note:   candidate expects 1 argument, 0 provided
class_construct.cpp:3:7: note: candidate: A::A(const A&)
 class A{
    
    
       ^
class_construct.cpp:3:7: note:   candidate expects 1 argument, 0 provided
@ubuntu:/mnt/hgfs/ub2/class$ 

Example 8-3, deep copy and shallow copy

Now that we know about parameterized constructors, let's discuss the copy constructor in Example 8-1 .

#include <stdio.h>

class A{
public:
	A(){
		printf("A\n");
	}
	A(int data)
	{
		printf("A(int data)\n");
	}
	~A(){
		printf("A~~~~~~~~~~~~~\n");
	}
};

int main()
{
	A  w(10);
	A  x = 10;
	A  y = x;
	A  z;
}

result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_5.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A(int data)
A(int data)
A
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
A~~~~~~~~~~~~~
@ubuntu:/mnt/hgfs/ub2/class$ 

According to the results, it can be known thatA y = x; Instead of executing the custom constructor content printf("A\n"), it executes the system's default blank constructor and then copies the data of object x as y's own data

The common saying is: the custom created object x has data, and the created object z is also custom, but the object y is created by the system and uses the data of the custom object x, which is equivalent to the scum data 10 in the example. Object x then goes to mess with object y . This is very problematic, right? Because one day object x finds out that this dregs has object y and then kills dregs (object x releases the data), the dregs being killed will definitely affect object y. This is shallow copy .

And deep copy is the real sense of data is divided into two, twins. The older brother has object x, and the younger brother has object y. One day the elder brother was drained by the subject x, but the younger brother is full of energy, and the subject y is not affected at all.

Here is a shallow copy that will fail:

#include <stdio.h>

class A{
public:
	A()
	{
		printf("A()\n");
		p = new char[10];
	}


	~A()
	{
		printf("~A()\n");
		delete [] p;
	}

private:
	char *p;
};

int main()
{
	A x;

	A y = x;

}

Error result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_6.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A()
~A()
~A()
*** Error in `./a.out': double free or corruption (fasttop): 0x00000000006e7030 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7f8211ee97f5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8038a)[0x7f8211ef238a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f8211ef658c]
./a.out[0x4008d5]
./a.out[0x40083a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f8211e92840]
./a.out[0x400729]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:2d 75348                              /mnt/hgfs/ub2/level14/1day/class/a.out
00600000-00601000 r--p 00000000 00:2d 75348                              /mnt/hgfs/ub2/level14/1day/class/a.out
00601000-00602000 rw-p 00001000 00:2d 75348                              /mnt/hgfs/ub2/level14/1day/class/a.out
006d5000-00707000 rw-p 00000000 00:00 0                                  [heap]
7f820c000000-7f820c021000 rw-p 00000000 00:00 0 
7f820c021000-7f8210000000 ---p 00000000 00:00 0 
7f8211b69000-7f8211c71000 r-xp 00000000 08:01 669534                     /lib/x86_64-linux-gnu/libm-2.23.so
7f8211c71000-7f8211e70000 ---p 00108000 08:01 669534                     /lib/x86_64-linux-gnu/libm-2.23.so
7f8211e70000-7f8211e71000 r--p 00107000 08:01 669534                     /lib/x86_64-linux-gnu/libm-2.23.so
7f8211e71000-7f8211e72000 rw-p 00108000 08:01 669534                     /lib/x86_64-linux-gnu/libm-2.23.so
7f8211e72000-7f8212032000 r-xp 00000000 08:01 669539                     /lib/x86_64-linux-gnu/libc-2.23.so
7f8212032000-7f8212232000 ---p 001c0000 08:01 669539                     /lib/x86_64-linux-gnu/libc-2.23.so
7f8212232000-7f8212236000 r--p 001c0000 08:01 669539                     /lib/x86_64-linux-gnu/libc-2.23.so
7f8212236000-7f8212238000 rw-p 001c4000 08:01 669539                     /lib/x86_64-linux-gnu/libc-2.23.so
7f8212238000-7f821223c000 rw-p 00000000 00:00 0 
7f821223c000-7f8212252000 r-xp 00000000 08:01 658907                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8212252000-7f8212451000 ---p 00016000 08:01 658907                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8212451000-7f8212452000 rw-p 00015000 08:01 658907                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8212452000-7f82125c4000 r-xp 00000000 08:01 1057509                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f82125c4000-7f82127c4000 ---p 00172000 08:01 1057509                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f82127c4000-7f82127ce000 r--p 00172000 08:01 1057509                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f82127ce000-7f82127d0000 rw-p 0017c000 08:01 1057509                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f82127d0000-7f82127d4000 rw-p 00000000 00:00 0 
7f82127d4000-7f82127fa000 r-xp 00000000 08:01 667754                     /lib/x86_64-linux-gnu/ld-2.23.so
7f82129d9000-7f82129df000 rw-p 00000000 00:00 0 
7f82129f8000-7f82129f9000 rw-p 00000000 00:00 0 
7f82129f9000-7f82129fa000 r--p 00025000 08:01 667754                     /lib/x86_64-linux-gnu/ld-2.23.so
7f82129fa000-7f82129fb000 rw-p 00026000 08:01 667754                     /lib/x86_64-linux-gnu/ld-2.23.so
7f82129fb000-7f82129fc000 rw-p 00000000 00:00 0 
7ffc6e46b000-7ffc6e48c000 rw-p 00000000 00:00 0                          [stack]
7ffc6e573000-7ffc6e576000 r--p 00000000 00:00 0                          [vvar]
7ffc6e576000-7ffc6e578000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)
@ubuntu:/mnt/hgfs/ub2/class$ 

There is double free or corruption in the above error message, which explains the problem. Object x applies for a piece of memory space, and object y uses the already applied memory space. Because it is executed twice during destructuring, that is, the memory is released twice and only one piece of memory is actually applied, so the error double free is reported.

The following is a deep copy:

#include <stdio.h>
#include <string.h>

class A{
public:
	A()
	{
		printf("A()\n");
		p = new char[10];
		strcpy(p, "hello");
	}
	A(const A &x)
	{
		printf("const A()\n");
		p = new char[10];
		strcpy(p, x.p);
	}

	~A()
	{
		printf("~A()\n");
		delete [] p;
	}

private:
	char *p;
};

int main()
{
	A x;

	A y = x;

}

result:

@ubuntu:/mnt/hgfs/ub2/class$ g++ class_7.cpp 
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out 
A()
const A()
~A()
~A()
@ubuntu:/mnt/hgfs/ub2/class$ 

From the perspective of code security, deep copy is necessary. In fact, there are similarities between deep and shallow copying and our copying and pasting. Shallow copying is equivalent to copying a shortcut, while deep copying requires copying of all file packages.

Guess you like

Origin blog.csdn.net/weixin_44035986/article/details/122784006