C + + object oriented and stack memory management

Writing object-oriented programs in C + + is very different from writing programs in other completely object-oriented languages. The basic reason is that C + + only passes values, not objects. In fact, this difference should be classified as "incomplete object-oriented language" and "complete object-oriented language". C + + is only one of the representatives of the former.

This series of articles take a simple database class as an example, and gradually realize the difference between "complete" and "incomplete".

The target group of this article is beginners of C + +. The purpose of writing is to explain the difference between "complete" and "incomplete", and to deepen your understanding of "encapsulation, inheritance and polymorphism" in object-oriented in the process of analyzing this case, so as to help you learn the idea of object-oriented.

Blogger is also a new programmer. If there is any omission, please forgive me and let me know.

#2.0 take on the improved version above

//Considering that reference parameters do not need to be copied, can reference parameters be used to improve program speed?

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

class Record
{
	friend ostream& operator<<(ostream& out, const Record& rec);
public:
	unsigned int id;
	unsigned int gpa;
	string name;
};
ostream& operator<<(ostream& out, const Record& rec)
{
	out << rec.id << ' ' << rec.name << ' ' << rec.gpa << endl;
	return out;
}

class Database
{
public:
	Record& operator[](size_t index)
	{
		return _datatab[index];
	}
	void insert(Record& rec)//Citation
	{
		_datatab.push_back(rec);
	}

private:
	vector<Record> _datatab;
};

/*
Database& import_file(string file_path)
{
	ifstream fin(file_path);
	Database db;
	Record temp;
	while (fin >> temp.id >> temp.name >> temp.gpa)
	{
		db.insert(temp);
	}
	return db;//Wrong! Can't return objects in the stack! Because before the return value reaches the calling function, the object has been destructed!
}
*/

Database& import_file(string file_path)
{
	ifstream fin(file_path);
	Database& db = *new Database;//Only in this way can we break the limitation of stack level
	Record temp;
	while (fin >> temp.id >> temp.name >> temp.gpa)
	{
		db.insert(temp);//Although the program runs correctly, there is a big logic error!
	}
	return db;
}

int main()
{
	//Database db = import_file("./RawData.txt"); / / congratulations, memory leak
	Database& db = import_file("./RawData.txt");//So that there is no memory leak
	for (int i = 0; i < 10; ++i)
	{
		cout << db[i];
	}
	delete& db;//At the same time, don't forget to release the heap space occupied by db!
	/*Here, a very pit appears:
	You have to know which functions return references to heap space objects. That is to say, you should know the root of the previous functions at any time
	You need to know that you may write many functions that return references, some are heap objects, some are stack objects and global objects that cannot be released (for example, < overload in line 18). Heap objects must be deleted, and stack objects must not be deleted
	In addition, sometimes an object may be used across several stacks. You must ensure that there is no delete before the last use, and you must delete after the last use
	Suppose you've written 10000 lines of code, can you still be as sober as you are now?! Lines 68 and 69 are very similar to stack objects. In 10000 lines of code, how to distinguish them!*/
}

/*Conclusion:
By referring to parameter passing, the problem of meaningless copy of object passing is solved
 But it also brings a new problem: because of the sensitivity of parameters to stack memory, developers need to be very clear about the stack location of return parameters
 In other words, the programmer must be clear about the information attached to the return value. When there are many functions and complicated parameter passing, it is extremely difficult to maintain this code!
If there's something wrong with the code here, debug is disgusting! Because the release depends on the pointer, which is why many books say that the "bad" side of the pointer*/

/*The problem is:
The root cause of the problem is that when the program is running, the memory is not homogeneous, and the stack memory and heap memory are very different. This difference is also inherited to the objects built on the memory
 Naturally, there are two ways to solve this problem:
1.Why do you want to divide stacks? Is it ok to use all stacks or all heaps? (not possible)
2.Can we use class mechanism to cover up stack differences? (automatic garbage collection)*/

2.1 proof code of memory leak

//Prove the memory leak of line 68 in 2

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

class Record
{
	friend ostream& operator<<(ostream& out, const Record& rec);
public:
	unsigned int id;
	unsigned int gpa;
	string name;
};
ostream& operator<<(ostream& out, const Record& rec)
{
	out << rec.id << ' ' << rec.name << ' ' << rec.gpa << endl;
	return out;
}

class Database
{
public:
	Record& operator[](size_t index)
	{
		return _datatab[index];
	}
	void insert(Record& rec)
	{
		_datatab.push_back(rec);
	}

private:
	vector<Record> _datatab;
};

Database* p;

Database& import_file(string file_path)
{
	ifstream fin(file_path);
	Database& db = *new Database;
	Record temp;
	while (fin >> temp.id >> temp.name >> temp.gpa)
	{
		db.insert(temp);
	}
	p = &db;
	return db;
}

int main()
{
	Database db = import_file("./RawData.txt");//Prove the memory leak here
	db[0] = db[1];
	cout << db[0];
	cout << (*p)[0];
	delete& db;//There will be a mistake here, too
}

#2.2 proof code of logic error

//Prove the logic error of line 61 in 2

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

class Record
{
	friend ostream& operator<<(ostream& out, const Record& rec);
public:
	Record() :name(new string)
	{
		//If the record contains a pointer to the heap data
	}
	~Record()
	{
		//Obviously, the heap memory should go with the object, otherwise the class will have no automaticity and encapsulation
		delete name;
	}
	unsigned int id;
	unsigned int gpa;
	string* name;
};
ostream& operator<<(ostream& out, const Record& rec)
{
	out << rec.id << ' ' << rec.name << ' ' << rec.gpa << endl;
	return out;
}

class Database
{
public:
	Record& operator[](size_t index)
	{
		return _datatab[index];
	}
	void insert(Record& rec)
	{
		_datatab.push_back(rec);
	}

private:
	vector<Record> _datatab;
};


Database& import_file(string file_path)
{
	ifstream fin(file_path);
	Database& db = *new Database;
	Record temp;
	while (fin >> temp.id >> *temp.name >> temp.gpa)
	{
		db.insert(temp);//By the second he was wrong
	}
	return db;
}

int main()
{
	Database& db = import_file("./RawData.txt");
	for (int i = 0; i < 10; ++i)
	{
		cout << db[i];
	}
	delete& db;
}

 

Tags: Database

Posted on Sun, 10 Nov 2019 10:13:46 -0800 by Liam-PHP