Careful: Passing STL containers as parameters might lead to MLE

Revision en2, by Mindeveloped, 2024-01-31 12:28:00

Hello, everyone. I'd like to share an issue I met recently of which passing an STL container as a parameter through a function call caused MLE.

Look at this submission: 244198211. I was debugging it when I realized such line is causing MLE.

void search (string str, int len) {

I thought passing a string as a parameter might be the problem, so I changed my code and passed the index instead, and it got AC (there are other bugs but they don't matter): 244220216

So why is the problem?

The problem is, passing a parameter in non-reference calls the copy constructor and the destructor of the object each once. Now this is particularly problematic with STL containers because STL containers use dynamic memory allocation. So when creating new objects in function calls, it does not always reuse previously free'd memory, leading extra memory usage in every call that stacks up.

To make it clear I did some experiment, shown below:

#include<iostream>
using namespace std;

struct node {
	node () {
		cout << "Constructor called" << endl;
	}
	node (node &obj) {
		cout << "Copy constructor called" << endl;
	}
	~node () {
		cout << "Destructor called" << endl;
	}
};
node x;
void normal (node y) {
	cout << "Normal function body" << endl;
}
void reference (node &y) {
	cout << "Reference function body" << endl;
}
int main () {
	cout << "Calling normal function" << endl;
	normal (x);
	cout << "Finished calling normal function. Calling reference function" << endl;
	reference (x);
	cout << "Finished calling reference function" << endl;
	return 0;
}

And the result is:

Constructor called
Calling normal function
Copy constructor called
Normal function body
Destructor called
Finished calling normal function. Calling reference function
Reference function body
Finished calling reference function
Destructor called

From upon you can see that passing object as normal parameter calls copy constructor and destructor each once, and passing as reference does not do anything to the object.

So here are some suggestions to avoid this:

  1. Pass indices or pointers instead of the original object. The straightforward way, though it might makes the code look bad (imo).
  2. Use arrays instead of strings or vectors. Arrays are passed as pointers of the initial address (in other ways, they are passed as references automatically).
  3. Pass reference if you can. It makes the code more readable compared to 1.

Thanks for reading.

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en3 English Mindeveloped 2024-01-31 14:12:27 622
en2 English Mindeveloped 2024-01-31 12:28:00 3 Tiny change: '.\n3. Passing reference' -> '.\n3. Pass reference'
en1 English Mindeveloped 2024-01-31 12:26:56 2629 Initial revision (published)