c ++中的“generics编程”的含义是什么?
generics编程在c ++中的含义是什么?
此外,我想弄清楚什么容器,迭代器 ,它们的不同types的意思。
generics编程意味着你不是编写按原样编译的源代码,而是编写编译过程中编译器转换为源代码的源代码“模板”。 generics编程最简单的例子是容器类,如数组,列表或包含其他对象集合的映射。 但是generics编程还有很多。 在C ++(也称为元编程 )中,它意味着编写在编译时被评估的程序。
generics编程的一个基本示例是容器的模板:在像C ++这样的静态types语言中,您将不得不声明容纳整数,浮点数和其他types的单独容器,或者处理指针void
并因此丢失所有types信息。 作为generics编程的C ++方式的模板通过让您定义在定义类时一个或多个参数未指定的类来利用此约束。 当你稍后实例化模板时,告诉编译器应该使用哪种types来创build模板外的类。 例:
template<typename T> class MyContainer { // Container that deals with an arbitrary type T }; void main() { // Make MyContainer take just ints. MyContainer<int> intContainer; }
模板是通用的,因为编译器将模板转换为实际的代码。 请注意,如果您没有实例化您的模板,则根本不会生成任何代码。 另一方面,如果声明MyContainer<int>
, MyContainer<float>
和MyContainer<String>
则编译器将创build三个版本的代码,每个代码具有不同的types。 将会涉及到一些优化,但基本上你的模板代码将立即被用于三种新的types。
迭代器是一种devise模式 ,在Gamma等人的开创性的“devise模式”一书中得到了推广。 这是迭代容器类的内容的一种模式。 与使用for
-loop不同,iterator是一个指向容器成员的类的实例,并为您提供统一的接口来遍历容器以及访问成员。 看看这个例子:
// Instanciate template QList with type int QList<int> myList; // put some ints into myList // Copyconstruct iterator that points to the // first member of the list. QList<int>::iterator i = myList.begin(); // Iterate through the list while (i != myList.end()) { std::cout << *i << std::endl; i++; }
在这个C ++示例中,我正在使用int
types实现一个模板 QList。 QList存储对象列表的容器类。 在这个例子中,我们将使用它来存储整数。
然后我创build一个迭代器来遍历列表。 myList.begin()
返回一个指向列表第一个元素的迭代器。 我们可以将迭代器与指向列表最后一个元素的另一个迭代器myList.end()
进行比较。 如果两个迭代器都是相同的,我们知道我们已经通过了最后一个元素。 在循环中,我们通过用*i
访问它来打印元素,并使用i++
转到下一个元素。
请注意,在这个例子中*
和++
是重载操作符,并被迭代器类重新实现。 在没有运算符重载的编程语言中,可能会有像执行相同任务的i.element()
或i.next()
这样的方法。 重要的是要看到, i
不是一个指针,而是一个模仿指针行为的整个类。
迭代器有什么好处? 它们提供了一个统一的方式来访问一个容器类的成员,完全独立于容器类如何在内部实现。 无论你想遍历列表,地图还是树,迭代器类(应该)总是以相同的方式工作。
容器
在C ++中,容器是一个允许存储对象的类。 例如,标准库std::vector<T>
是一个可resize的数组,它存储了某些types为T的对象。为了正式被认为是一个容器类,它必须公开某些function以便于generics编程。 我可以引用C ++标准中的确切要求,但是对于大多数情况来说,与标准库相关的容器类是: vector
, deque
, list
, map
, set
和multimap
/ multiset
。
其中一个重要的要求是它们必须允许迭代器访问。
迭代器
“迭代器”在这里意味着两件事:它是devise模式的名称,但在C ++中,它也是该devise模式的特定expression的名称。 C ++迭代器是一种允许使用类似指针的语法遍历一系列元素的types。
例如,如果你有一个数组int a[10]
,你可以使用一个普通指针作为迭代器:
int* first = a; // create an iterator that points to the beginning of the array ++first; // make the iterator point to the second element int i = *first; // get the value of the element pointed to by the iterator int* last = a+10; //create an "end" iterator, one which points one past the end of the array
如果我有一个链表,如std::list<int> l
,我可以做很多相同的工作,虽然现在我的迭代器不再只是指针,而是实现了一个专门用于std::list
的类types:
std::list<int>::iterator first = l.begin(); // create an iterator that points to the beginning of the list ++first; // make the iterator point to the second element int i = *first; // get the value of the element pointed to by the iterator std::list<int>::iterator last = l.end(); //create an "end" iterator, one which points one past the end of the list
或与一个向量std::vector<int> v
:
std::vector<int>::iterator first = v.begin(); // create an iterator that points to the beginning of the vector ++first; // make the iterator point to the second element int i = *first; // get the value of the element pointed to by the iterator std::list<int>::iterator last = v.end(); //create an "end" iterator, one which points one past the end of the vector
迭代器的重要之处在于,它们为我们提供了遍历元素序列的统一语法, 而不pipe序列是如何存储在内存中的 (或者甚至是存储在内存中的)。可以写一个迭代器来迭代数据库在磁盘上,或者我们可以使用迭代器包装来创build一个stream,如std::cin
看起来也像一系列的对象:
std::istream_iterator<int>(std::cin) first; ++first; // make the iterator point to the second element int i = *first; // get the value of the element pointed to by the iterator std::list<int>::iterator last; //create an "end" iterator, which marks the end of the stream
虽然因为这包装了一个常规的stream,但它是一个更有限的迭代器types(例如,不能向后移动,这意味着并不是所有以下algorithm都适用于stream迭代器。
现在,给定任何这些迭代器types,我们可以使用所有devise用于迭代器的标准库algorithm。 例如,要查找序列中值为4
的第一个元素:
std::find(first, last, 4); // return the first iterator which equals 4 and which is located in the interval [first, last)
或者我们可以sorting序列(不适用于stream迭代器):
std::sort(first, last);
或者如果我们编写一个函数,这个函数就是一个整数,例如:
int square(int i) { return i * i; }
那么我们可以将其应用于整个序列:
// for every element in the range [first, last), apply the square function, and output the result into the sequence starting with first std::transform(first, last, first, square);
这就是迭代器的优点:它们抽象出容器的细节,以便我们可以对任何序列应用generics操作。 感谢迭代器,相同的find
或sort
实现与链接列表以及数组一起工作,甚至与您自己的自制容器类一起工作。
generics编程
generics编程基本上是你的代码应该尽可能通用的想法。 正如上面的迭代器示例所示,我们想出了一个types必须支持的通用的一组函数,以便被称为迭代器,然后我们编写可用于任何迭代器types的algorithm。
将其与传统的面向对象编程相比较,迭代器必须通过inheritance某种IIterator
接口来“certificate”它们是迭代器。 这将阻止我们使用原始指针作为迭代器,所以我们会失去通用性。
在C ++中,使用generics编程,我们不需要官方的界面。 我们只是使用模板编写algorithm,所以它们接受任何 类似于迭代器的types,而不pipe它们在何处,何时以及如何定义,以及它们是否来自公共基类或接口。
在最简单的定义中,generics编程是计算机编程的一种风格,其中algorithm是按照待指定的types编写的,然后在需要时作为参数提供的特定types实例化。
作为历史重点,模板之前的C ++版本是该语言的一部分,它包含一个“ generic.h ”,其中包含可以扩展为类声明的预处理器macros。 所以你可以有一个类的通用模式(“模板”),你可以通过传递一些参数到macros中,当你把它们扩展到实际的类声明时,它可以改变。 但是,预处理macros不是types安全的,处理起来有点笨拙,而且由于这些原因,在C ++代码中的使用大大减less了。 C ++采用了更通用的模板作为语言的元素,但是“通用”编程这个术语仍然存在。 “generics”现在在其他编程语言中被用作美化types转换。 除此之外,这个问题已经被熟练地回答了。
generics编程:几乎只涉及模板。
container(容器):一个结构或类,它包含自己的数据和作用于该数据的方法。
迭代器:它是一个指向你可以遍历的一些内存地址的指针(就像一个数组)。
纠正我,如果上述任何错误。
types参数的概念,这使得可以devise类和方法来推迟一种或多种types的规范,直到类或方法被客户端代码声明和实例化为止。