Simple understanding of C++ range for loop

The range for loop is a new feature of C++11. It can be used in various C++ containers such as vector and map. It is very convenient to traverse all elements of the container. I found the range for loop in the process of manually implementing the array class. lower level mechanism. Writing a blog is just a record of my own learning. If it happens to help a beginner understand the mechanism, it is an honor for Bencaicai.

Manually implement the array class reference Manually simulate the array class in C++ stl - Harris_ayaka's blog - CSDN blog

grammar

Go directly to the code:

#include<iostream>
#include<vector>
int main() {
	std::vector<int> nums{ 1,2,3,4,5,6,7,8 };
	for (int num : nums) {
		std::cout << num << std::endl;
	}
}

Output result:

It can be seen that this writing method is very convenient when you need to traverse the container completely and do not need to use subscripts.

use mechanism

If you want to modify the value of a variable, use a reference type:

for (int& num : nums) {//declared as a reference type
	num += 114514;
	std::cout << num << std::endl;
}

  result:

Here it is recommended that if the value of the variable in the container is not modified, it is also declared as a reference type of const:

for (const int& num : nums) {
	std::cout << num << std::endl;
}

The reason for this is briefly explained below. First, customize a class and overload the output stream operator (to save some trouble for output~)

#include<iostream>
#include<vector>
template<class Type>
class complex {
private:
	Type x;
	Type y;
public:
	complex(Type x = Type(), Type y = Type()) :x(x), y(y) {
		std::cout << "constructor call" << std::endl;
	}
	complex(const complex& right) {
		x = right.x;
		y = right.y;
		std::cout << "copy constructor call" << std::endl;
	}
	 
	template<class Type>
	friend std::ostream& operator<<(std::ostream &c, const complex<Type>& out);
};
template<class Type>
std::ostream& operator<<(std::ostream &c, const complex<Type>& out){
    //Overloaded the output stream for easy output
	c << out.x << "," << out.y;
	return c;
}

Then test with the following main function

int main() {
	std::vector<complex<int>> nums;
	nums.reserve(10);
	nums.emplace_back(1, 2);
	nums.emplace_back(3, 4);
	nums.emplace_back(5, 6);
	nums.emplace_back(7, 8);
	nums.emplace_back(9, 10);
	for (complex<int> num : nums) {
		std::cout << num << std::endl;
	}
}

Take a look at the output:

The copy constructor is called five times, that is, one copy before each output. To avoid unnecessary copying, use reference types instead:

for (complex<int>& num : nums) {
	std::cout << num << std::endl;
}

 

The simple principle eliminates the need for copying. Therefore, whether or not to modify the value of the variable in the container, it is recommended to use a reference. If no modification is required, use the const keyword to modify it.

internal mechanism

Question: If I customize a container (you can refer to the manually simulated array container mentioned above), if I want to use such a for loop, how do I need to implement it?

In the process of my own implementation, I came to a conclusion that it is the iterator. In the custom container type, as long as the iterator with the same interface as the standard library is defined, the range for loop can be used (need to define the iterator type, begin(), and end() )

To demonstrate this mechanism, let's quickly implement a castrated sequential container manually:

class array {
private:
	int* list;
	unsigned int size;
public:
	array(unsigned int n = 0) {
		list = new int[n];
		size = n;
		for (int i = 0; i < n; ++i) {
			list[i] = 0;
		}//Simply initialize each value to 0;
	}
	int& operator[](const unsigned int &idx) {
		return *(list + idx);
	}//Overloading the subscript operator
};

If at this point I use a range for loop on this container:

int main() {
	int n = 10;
	array nums(n);
	for (int &num : nums) {
		num = 5;
		std::cout << num << std::endl;
	}
}

He reported an error like this:

So we define an iterator ourselves:

class array {
private:
	int* list;
	unsigned int size;
public:
	array(unsigned int n = 0) {
		list = new int[n];
		size = n;
		_begin = list;
		_end = list + n;
		for (int i = 0; i < n; ++i) {
			list[i] = 0;
		}//Simply initialize each value to 0;
	}
	~array() {
		delete[]list;
	}//Added destructor, just didn't add it because of negligence qwq
	int& operator[](const unsigned int &idx) {
		return *(list + idx);
	}//Overloading the subscript operator
	typedef int* iterator;//Defines an iterator of the array container type
	iterator begin() {
		return _begin;
	}
	iterator end() {
		return _end;
	}
private:
	iterator _begin;
	iterator _end;
};

Using the same test function again, there is no error

int main() {
	int n = 10;
	array nums(n);
	for (int &num : nums) {
		num = 5;
		std::cout << num << std::endl;
	}
}

get the following output

As mentioned in the above error report, only the begin() interface is needed, then try deleting the iterator and leaving only begin() and end() to complete the range for loop:

The internal form is as follows

int* begin() {
	return list;
}
int* end() {
	return list + size;
}

still got the correct result

Tip: If you only keep begin, an error will be reported

In the definition, iterators are generalized pointers, which means that pointers are actually special iterators. So if a custom container wants to use a range for loop, it must define an iterator and provide begin() and end() interfaces

For a range for loop can also be equivalent to the following code

for (int& num : nums) {
	std::cout << num << std::endl;
}
//The two spellings are equivalent
for (std::vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
	int& num = *it;
	std::cout << num << std::endl;
}

Summarize

Range for loops can be very difficult to use in some places, especially when you need to operate on subscripts in the loop, or when you don't need to traverse all the elements of the container, but in certain cases it is very neat to write, especially when replacing iterators , very concise. This time, I dared to simply dig the underlying mechanism and wrote a blog. If there is something wrong, please criticize and correct me.

Tags: C++ programming language

Posted by inspire on Fri, 03 Jun 2022 23:28:36 +0530