@Moritz
2019-01-25T04:36:07.000000Z
字数 27533
阅读 442
C++primer
C++
课程学习
所有文稿
using
Declarationsscope operation::
: std::cin
says that we want to use the name cin
from namespace std
.
A using declarations lets us use a name from a namespace **without qualifying the name with a prefix前缀
like namespace_name::
.
string
TypeA string is a variable-length sequence of characters. Because it is part of the library, string
is defined in the std
namespace.
#include <string>
using std::string;
/*
using namespace std;
*/
More string
operations in 9.5
string s2(s1); // s2 is a copy of s1
string s2 = s1; // s2 is a copy of s1
string s3("hiya"); // s3 is a copy of the string literal, not including the null
string s3 = "hiya"; // equivalent to s3("hiya")
string s4(10, 'c'); // s4 is cccccccccc
When we initialize a variable using =
, we are asking the compiler to copy initialize拷贝初始化
the object by copying the initializer 初始化值
on the right-hand side into the object being created.
string s2 = s1;
string s3 = "hiya";
Otherwise, when we omit the =
, we use direct initialization 直接初始化
.
string s3("hiya");
string s4(10, 'c');
cin>>string
空白字符
while(cin>>string)
getline(cin,string)
include whitespace
stop when sees a newline
not including the newline
return its istream
argument 返回流参数
// read input a line at a time until end-of-file
while (getline(cin, line))
cout << line << endl;
empty
and size
operations
while (getline(cin, line))
if (!line.empty()&&line.size()>80)
cout << line << endl;
The string::size_type
Type
The size
member returns a string::size_type
value. It is an unsigned type although we don’t know the precise type of it.
comparing
The relational operators <, <=, >, >=
can be used to compare two strings: compare the first different character or the length of them, in the case of shorter string is equal to the corresponding character of the longer string.
assignment for strings
赋值
string s1(10,'c'),s2;
s2=s1; //cccccccccc
addition
+
or compound assignment operator += 复合赋值运算符
When we mix strings s1,s2...
and string or character literals "Hello",'a'...字符串字面值和字符字面值
, at least one operand to eachoperator must be of string
type
string s6 = s1 + ", " + "world"; // ok: each + has a string operand
/*It is equivalent to these 2 sentences*/
string temp = s1 + ", "; // ok: + has a string operand
s6 = temp + "world"; // ok: + has a string operand
string s7 = "hello" + ", " + s2; // error: can't add string literals
string s7 = ("hello" + ", ") + s2; // error: can't add string literals
Knowing and/or changing thecharacteristics of a character is handled by a set of library
functions, defined in the cctype
header .
isalnum(c) | 字母或数字 |
---|---|
isalpha(c) | 字母 |
isdigit(c) | 数字 |
iscntrl(c) | 控制字符 |
isgraph(c) | 不是空格但可打印时为真 |
islower(c) | 小写字母 |
isupper(c) | 大写字母 |
ispunct(c) | 标点符号 |
isspace(c) | 空白(即空格、横向/纵向制表符、回车符、换行符、进制符中的一种) |
isprint(c) | 可打印字符(空格或具有可视形式) |
isxdigit(c) | 十六进制数字 |
tolower(c) | 若c是大写,则输出小写;否则原样输出c |
toupper(c) | 若c是小写,则输出大写;否则原样输出c |
Use Range-Based for
to Processing Every Character:
string str("some string");
// print the characters in str one character to a line
for (auto c : str) // for every char in str
cout << c << endl; // print the current character followed by a newline
string s("Hello World!!!");
// punct_cnt has the same type that s.size returns (string::size_type)
decltype(s.size()) punct_cnt = 0;
// count the number of punctuation characters in s
for (auto c : s) // for every char in s
if (ispunct(c)) // if the character is punctuation
++punct_cnt; // increment the punctuation counter
cout << punct_cnt<< " punctuation characters in " << s << endl;
/*output:
3 punctuation characters in Hello World!!!
*/
string s("Hello World!!!");
// convert s to uppercase
for (auto &c : s) // for every char in s (note: c is a reference)
c = toupper(c); // c is a reference 引用, so the assignment changes the char in s
cout << s << endl;
/*The output of this code is
HELLO WORLD!!!
*/
There are two ways to access individual characters in a string: We can use a subscript 下标
or an iterator 迭代器
. The subscript operator [ ]
takes a string::size_type
value, called subscript or index 索引
. Before accessing the character, we check that string is not empty.
vector
TypeA vector
is often referred to as a container 容器
because it “contains” other objects.
#include <vector>
using std::vector;
A vector is a class template 类模板
. C++ has both class and function templates 函数模板
. Templates 模板
are not themselves functions or classes. Instead, they can be thought of as instructions 说明
to the compiler for generating classes or functions. The process that the compiler uses to create classes or functions from templates is called instantiation 实例化
. When we use a template, we specify what kind of class or function we want the compiler to instantiate.
For a class template, we specify which class to instantiate by supplying additional information, the nature of which depends on the template. How we specify the information is always the same: We supply it inside a pair of angle brackets 尖括号
following the template’s name.
In the case of vector, the additional information we supply is the type of the objects the vector will hold:
#include <vector>
using std::vector;
vector<int> ivec; // ivec holds objects of type int
vector<Sales_item> Sales_vec; // holds Sales_items
vector<vector<string>> file; // vector whose elements are vectors,C++11
vector<vector<string> > file; //earlier than C++11, add a space between brackets
Because references
are not objects, we cannot have a vector of references.
vector<T> v1; //空vector,潜在的元素是T类型,执行默认初始化
vector<T> v2(v1); //与下一行代码等价
vector<T> v2=v1;
vector<T> v3(n,value);
vector<T> v4(n); //包含了n个重复地执行了初始化的对象
vector<T> v5{a,b,c...};
vector<T> v5={a,b,c...}; //与下一行代码等价
Some restrictions in several forms of initialization 初始化
provided by C++ ( haven't list all )
when we use the copy initialization 拷贝初始化
form(using =
), we can supply only a single initializer
and when we supply an in-class initializer 内类初始值
, we must either use copy initialization or use curly braces花括号
we can supply a list of element values only by using list initialization in which the initializers are enclosed in curly braces
vector<string> v2("a", "an", "the"); // error
Value Initialization 值初始化
We can usually omit the value and supply only a size. In this case the library 库
creates a value-initialized element initializer 值初始化的元素初值
for us. This library-generated value is used to initialize each element in the container. The value of the element initializer depends on the type of the elements stored in the vector.
If the vector holds elements of a built-in type 内置类型
, such as int
, then the element initializer has a value of 0
. If the elements are of a **class type 类类型
**, such as string
, then the element initializer is itself default initialized元素由类默认初始化
:
vector<int> ivec(10); // ten elements, each initialized to 0
vector<string> svec(10); // ten elements, each an empty string
There are two restrictions on this form of initialization:
The first restriction is that some classes require that we always supply an explicit initializer 明确的初始值
. If our vector holds objects of a type that we cannot default initialize 不支持默认初始化
, then we must supply an initial element value; it is not possible to create vectors of such types by supplying only a size.
The second restriction is that when we supply an element count 元素数量
without also supplying an initial value 初始值
, we must use the direct form of initialization 直接初始化
vector<int> vi = 10; // error: must use direct initialization to supply a size
Here we are using 10 to instruct vector how to create the vector—we want a
vector with ten value-initialized elements. We are not “copying” 10 into the vector.
Hence, we cannot use the copy form of initialization.
List Initializer 列表初始值
or Element Count?
In a few cases, what initialization means depends upon whether we use curly braces 花括号
or parentheses 圆括号
to pass the initializer(s).
vector<int> v1(10); // v1 has ten elements with value 0
vector<int> v2{10}; // v2 has one element with value 10
vector<int> v3(10, 1); // v3 has ten elements with value 1
vector<int> v4{10, 1}; // v4 has two elements with values 10 and 1
When we use parentheses ()
, we are saying that the values we supply are to be used to construct 构造
the object. When we use curly braces {}
, we’re saying that, if possible, we want to list initialize 列表初始化
the object. That is, if there is a way to use the values inside the curly braces as a list of element initializers, the class will do so.即优先考虑列表初始化
Only if it is not possible to list initialize the object will the other ways to initialize the object be considered.
On the other hand, if we use braces and there is no way to use the initializers to list initialize the object, then those values will be used to construct the object.
vector<string> v5{"hi"}; // list initialization: v5 has one element
vector<string> v6("hi"); // error: can't construct a vector from a string literal
vector<string> v7{10}; // v7 has ten default-initialized elements
vector<string> v8{10, "hi"}; // v8 has ten elements with value "hi"
Only v5
is list initialized. We cannot use an int
to initialize a string, so the initializers for v7
and v8
can’t be element initializers. If list initialization isn’t possible, the compiler looks for other ways to initialize the object from the given values.
we can use a vector member vector成员函数
named push_back
to takes a value and “pushes” that value as a new last element onto the “back” of the vector.
vector<int> v2; // empty vector
for (int i = 0; i != 100; ++i)
v2.push_back(i); // append sequential integers to v2 依次把整数放到v2尾端
// at end of loop v2 has 100 elements, values 0 . . . 99
Key Concept:
vectors
Grow EfficientlyThe standard requires that vector implementations can efficiently add
elements at run time. It is often unnecessary to define a vector of a
specific size except all the elements actually need the same value.
Programming Implications of Adding Elements to a vector
对vector对象添加元素蕴含的编程假定
Other implications that follow from the dynamic nature of vectors will become clearer as we learn more about using them. However, there is one implication that is worth noting already: For reasons we’ll explore in § 5.4.3 (p. 188), we cannot use a range for 范围for
if the body of the loop adds elements to the vector.
Some vector
Operations
We access the elements of a vector the same way that we access the characters in a string: through their position in the vector. For example, we can use a range for
to process all the elements in a vector:
vector<int> v{1,2,3,4,5,6,7,8,9};
for (auto &i : v) // for each element in v (note: i is a reference)
i *= i; // square the element value
for (auto i : v) // for each element in v
cout << i << " "; // print the element
The size
member returns a value of the size_type
defined by the corresponding vector type. To use size_type
, we must name the type in which it is defined.
vector<int>::size_type // ok
vector::size_type // error
Subscripting Does Not Add Elements
The subscript operator on vector
( and string
) fetches an existing element. It does not add an element. Subscript Only Elements that are Known to Exist!
Attempting to subscript elements that do not exist is, unfortunately, an extremely common and pernicious programming error. So-called **buffer overflow errors 缓冲区溢出
** are the result of subscripting elements that don’t exist. Such bugs are the most common cause of security problems in PC and other
applications.
Tip : A good way to ensure that subscripts are in range is to avoid subscripting altogether by using a
range for
whenever possible.
vector<int> ivec; // empty vector
for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
All of the library containers have iterators, but only a few of them support the subscript operator. Technically speaking, a string
is not a container type, but string
supports many of the container operations. Strings and vectors have a subscript operator and iterators.
We can use an iterator to fetch an element and iterators have operations to move from one element to another. As with pointers, an iterator may be valid or invalid 有效与无效之分
.
Unlike pointers, we do not use the address-of operator 取地址运算符
to obtain an iterator. Instead, types that have iterators have members that return iterators. In particular, these types have members named begin
and end
. The begin
member returns an iterator that denotes the first element (or first character) if there is one.
// the compiler determines the type of b and e
// b denotes the first element and e denotes one past the last element in v
auto b = v.begin(), e = v.end(); // b and e have the same type
The iterator returned by end
is an iterator positioned “one past the end” 尾元素的下一个位置
of the associated container (or string). This iterator denotes a nonexistent element “off the end” 尾后元素
of the container. It is used as a marker indicating when we have processed all the elements. The iterator returned by end
is often referred to as the off-the-end iterator 尾后迭代器
or abbreviated as the end iterator 尾迭代器
. If the container is empty, begin
returns the same ( off-the-end ) iterator as the one returned by end
.
Iterator Operations
*iter
: Returns a reference to the element denoted by the interator iter
Dereferencing an invalid iterator or an off-the-end iterator has undefined behavior.
string s("some string");
if (s.begin() != s.end()) { // make sure s is not empty
auto it = s.begin(); // it denotes the first character in s
*it = toupper(*it); // make that character uppercase
}
cout<<s<<endl;//"Some string"
iter->mem
/(*iter).mem
: Dereferences 解引用
iter
and fetcheds the member named mem
from the underlying element
When we dereference an iterator, we get the object that the iterator denotes. If that object has a class type, we may want to access a member of that object. For example, we might have a vector
of strings
and we might need to know whether a given element is empty. Assuming it
is an iterator into thisvector
, we can check whether the string
that it
denotes is empty as follows:
(*it).empty() // dereferences it and calls the member empty on the resulting object
*it.empty(); // error: attempts to fetch the member named empty from it
// but it is an iterator and has no member named empty
// print each line in text up to the first blank line
for (auto it = text.cbegin();it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
++iter
: Increments iter
to refer to the next element in the container
Because the iterator returned from end does not denote an element, it may not be incremented or dereferenced.
--iter
: Decrements iter
ot refer to the previous element in the container
iter1!=iter2``iter1==iter2
Key Concept: Generic Programming
泛型编程
Programmers coming to C++ from C or Java might be surprised that we used!=
rather than<
in our for loops. C++ programmers use!=
as a matter of habit for the same reason that they use iterators rather than subscripts: This coding style applies equally well to various kinds of containers provided by the library.
Iterator Types
As with size_type, we generally do not know—and do not need to know—the precise type of an iterator.
vector<int>::iterator it; // it can read and write vector<int> elements
string::iterator it2; // it2 can read and write characters in a string
vector<int>::const_iterator it3; // it3 can read but not write elements
string::const_iterator it4; // it4 can read but not write characters
A const_iterator
behaves like a const
pointer. A const_iterator
may read but not write the element it denotes; an object of type iterator
can both read and write.
If a vector
or string
is const, we may use only its const_iterator type.
The type returned by begin
and end
depends on whether the object on which they operator is const
. If the object is const
, then they return a const_iterator
; if the object is not const, they return iterator
.
vector<int> v;
auto it2 = cv.begin(); // it2 has type vector<int>::const_iterator
const vector<int> cv;
auto it1 = v.begin(); // it1 has type vector<int>::iterator
auto it3 = v.cbegin(); //C++11, it3 has type vector<int>::const_iterator
For reasons we’ll explain in § 6.2.3 (p. 213), it is usually best to use a const
type (such as const_iterator
) when we need to read but do not need to write to an object. 只读不写
Terminology
术语
: Iterators and Iterator Types
The term iterator is used to refer to three different entities. We might mean the concept of an iterator, or we might refer to the iterator type defined by a container, or we might refer to an object as an iterator.What’s important to understand is that there is a collection of types that are related conceptually. A type is an iterator if it supports a common set of actions. Those actions let us access an element in a container and let us move from one element to another.
Each container class defines a type named iterator; that iterator type supports the actions of an (conceptual) iterator
Some vector
Operations Invalidate Iterators
We noted that there are implications 副作用
of the fact that vectors can grow dynamically. We also noted that one such implication is that we cannot add elements to a vector inside a range for loop. Another implication is that any operation, such as push_back
, that changes the size of a vector potentially invalidates all iterators into that vector 使迭代器失效
. We’ll explore how iterators become invalid in more detail in § 9.3.6 .Loops that use iterators should not add elements to the container to which the iterators refer.
Iterators for string
and vector
support additional operations that can move an iterator multiple elements at a time. They also support all the relational operators. These operations are often referred to as iterator arithmetic 迭代器运算
.
iter+n``iter-n``iter+=n``iter-=n
: The result is still an iterator.iter1-iter2
: Subtracting two iterators yields the number that added to the right-hand iterator yields the left-hand iterators. The iterators must denote elements in, or one past the end of 尾元素的下一个元素
, the same container. The result type is a signed integral type named difference_type
. Both vector
and string
define difference_type
.>,>=,<,<=
: Relational operators on iterators.A classic algorithm that uses iterator arithmetic is binary search 二分搜索
.
My version:
cin>>tar;
auto head=v.begin(),tail=v.end();
while(head!=tail)
{
if (tar==*((tail-head)/2+head)) {flag=true;break;}
else if (tar>*((tail-head)/2+head)) head=(tail-head)/2+head;
else tail=(tail-head)/2+head;
}
Text book version:
// text must be sorted
// beg and end will denote the range we're searching
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg)/2; // original midpoint
// while there are still elements to look at and we haven't yet found sought
while (mid != end && *mid != sought) {
if (sought < *mid) // is the element we want in the first half?
end = mid; // if so, adjust the range to ignore the second half
else // the element we want is in the second half
beg = mid + 1; // start looking with the element just after mid
mid = beg + (end - beg)/2; // new midpoint
}
内置数组
Arrays are a compound type 复合类型
. The dimension must be known at compile 编译
time, which means that the dimension must be a constant expression 常量表达式
.
By default 默认情况下
, the elements in an array are default initialized 默认初始化
(§ 2.2.1, p. 43).
Warning
As with variables of built-in type, a default-initialized array of built-in type that is defined inside a function will have undefined values.和内置类型一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数字含有未定义的值
/*Exercise 3.28*/
string sa[10]; //空字符串
int ia[10]; //0
int main() {
string sa2[10]; //空字符串 ????
int ia2[10]; //元素的值全部未定义
}
When we define an array, we must specify a type for the array. We cannot use auto
to deduce the type from a list of initializers. As with vector
, arrays hold objects. Thus, there are no arrays of references.
Explicitly Initializing Array Elements 显式初始化数组元素
When we list initialize 列表初始化
the elements in an array, we can omit the dimension.If the dimension is greater than the number of initializers, the initializers are used for the first elements and any remaining elements are value initialized 被初始化为默认值
.
int a3[5] = {0, 1, 2}; // equivalent to a3[] = {0, 1, 2, 0, 0}
string a4[3] = {"hi", "bye"}; // same as a4[] = {"hi", "bye", ""}
Character arrays have an additional form of initialization: We can initialize such arrays from a string literal 字符串字面值
. When we use this form of initialization, it is important to remember that string literals end with a null character. That null character is copied into the array along with the characters in the literal.
char a1[] = {'C', '+', '+'}; //3, list initialization, no null
char a2[] = {'C', '+', '+', '\0'}; //4, list initialization, explicit null
char a3[] = "C++"; //4, null terminator added automatically
const char a4[6] = "Daniel"; // error: no space for the null!
No Copy or Assignment
We cannot initialize an array as a copy of another array, nor is it legal to assign one array to another.
int a[] = {0, 1, 2}; // array of three ints
int a2[] = a; // error: cannot initialize one array with another
a2 = a; // error: cannot assign one array to another
Warning
Some compilers allow array assignment as a compiler extension编译器扩展
. It is usually a good idea to avoid using nonstandard features. Programs that use such features, will not work with a different compiler.
int *ptrs[10]; // ptrs is an array of ten pointers to int
int &refs[10] = /* ? */; // error: no arrays of references
int (*Parray)[10] = &arr; // Parray points to an array of ten ints
int (&arrRef)[10] = arr; // arrRef refers to an array of ten ints
int *(&arry)[10] = ptrs; // arry is a reference to an array of ten pointers
By default, type modifiers 类型修饰符
bind right to left. Because the array dimension follows the name being declared, it can be easier to read array declarations by starting with the array’s name and reading from the inside out rather than from right to left.
Reading the declaration of int *(&arry)[10] = ptrs;
from the inside out, we see that arry
is a reference. Looking right, we see that the object to which arry
refers is an array of size 10. Looking left, we see that the element type is pointer to int
. Thus, arry
is a reference to an array of ten pointers.
When we use a variable to subscript an array, we normally should define that variable to have type size_t
. size_t
is a machine-specific 机器相关
unsigned type that is guaranteed to be large enough to hold the size of any object in memory. The size_t type
is defined in the cstddef
header,
As in the case of string
or vector
, it is best to use a range for
when we want to traverse 遍历
the entire array.
The compiler ordinarily converts the array to a pointer.
string *p2 = nums; // equivalent to p2 = &nums[0]
There are various implications of the fact that operations on arrays are often really operations on pointers. One such implication is that when we use an array as an initializer for a variable defined using auto
, the deduced type is a pointer, not an array.
int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
auto ia2(ia); // ia2 is an int* that points to the first element in ia
ia2 = 42; // error: ia2 is a pointer, and we can't assign an int to a pointer
Although ia
is an array of ten ints, when we use ia
as an initializer, the compiler treats that initialization as if we had written:
auto ia2(&ia[0]); // now it's clear that ia2 has type int
It is worth noting that this conversion does not happen when we use decltype
. The type returned by decltype(ia)
is array of ten ints:
// ia3 is an array of ten ints
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
ia3 = p; // error: can't assign an int* to an array
ia3[4] = i; // ok: assigns the value of i to an element in ia3
Pointers Are Iterators
Pointers to array elements support the same operations as iterators on vectors
or strings
and have additional operations beyond those we described before.( § 2.3.2 ) We can obtain an off-the-end pointer 尾后指针
by using another special property of arrays.
int arr[10];
int *e=&arr[10]; // pointer just past the last element in arr
Although we can compute an off-the-end pointer, doing so is error-prone 极易出错
. To make it easier and safer to use pointers, the new library includes two functions, named begin
and end
, defined in the iterator
header . However, arrays are not class types 类类型
, so these functions are not member functions 成员函数
. Instead, they take an argument that is an array 数组作为参数
:
int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
int *beg = begin(ia); // pointer to the first element in ia
int *last = end(ia); // pointer one past the last element in ia
In particular, we may not dereference or increment an off-the-end pointer. In a case using begin
and end
to process elements with a loop, the while
condition uses p_end
to know whether it is safe to dereference p_beg
.
Pointer Arithmetic
Pointers that address array elements can use all the iterator operations listed before in this chapter.
We can use the relational operators to compare pointers that point to elements of an array, or one past the last element in that array. We cannot use the relational operators on pointers to two unrelated objects.
Pointer arithmetic is also valid for null pointers 空指针
and for pointers that point to an object that is not an array.
Interaction between Dereference and Pointer Arithmetic 解引用和指针运算的交互
The result of adding an integral value to a pointer is itself a pointer. Assuming the resulting pointer points to an element, we can dereference the resulting pointer:
int ia[] = {0,2,4,6,8}; // array with 5 elements of type int
int last = *(ia + 4); // ok: initializes last to 8, the value of ia[4]
Subscripts and Pointers
int ia[] = {0,2,4,6,8}; // array with 5 elements of type int
int *p = &ia[2]; // p points to the element indexed by 2
int k = p[-2]; // p[-2] is the same element as ia[0]
Warning
Unlike subscripts forvector
andstring
, the index of the built-in subscript operator is not an unsigned type. The index used with the built-in subscript operator can be a negative value.内置的下标运算符可以处理负值
Warning
Although C++ supports C-style strings, they should not be used by C++ programs. C-style strings are a surprisingly rich source of bugs and are the root cause of many security problems. They’re also harder to use!
Character string literals 字符串字面值
are an instance of a more general construct that C++ inherits from C: C-style character strings C风格字符串
. C-style strings are not a type. Instead, they are a convention for how to represent and use character strings. Strings that follow this convention are stored in character arrays and are null terminated 空字符结束
, meaning that the last character in the string is followed by a null character '\0'
.
The rest of this section has not been learned and taken note for the disadvantages of using C-style strings.
/*Exercise 3.37: What does the following program do?*/
const char ca[] = {'h', 'e', 'l', 'l', 'o'};
const char *cp = ca;
while (*cp) {
cout << *cp << endl;
++cp;
}
/*According to [https://github.com/huangmingchuan/Cpp_Primer_Answers/blob/master/ch03/README.md]*/
/*会将ca 字符数组中的元素打印出来。但是因为没有空字符的存在,程序不会退出循环。*/
/*test result in CodeBlocks:
h
e
l
l
o
,
(
Process returned 0 (0x0) execution time : 0.104 s
Press any key to continue.
*/
/*What happended?*/
与旧代码的接口
The rest of this section has not been learned and taken note for the disadvantages of using C-style strings.
Strictly speaking, there are no multidimensional arrays in C++. What are commonly referred to as multidimensional arrays are actually arrays of arrays.
int ia[3][4]; // array of size 3; each element is an array of ints of size 4
// array of size 10; each element is a 20-element array whose elements are arrays of 30 ints
int arr[10][20][30] = {0}; // initialize all elements to 0
We can more easily understand these definitions by reading them from the inside out.We start with the name we’re defining ia
and see that ia
is an array of size 3. Continuing to look to the right, we see that the elements of ia
also have a dimension. Thus, the elements in ia
are themselves arrays of size 4. Looking left, we see that the type of those elements is int
. So, ia
is an array of size 3, each of whose elements is an array of four ints
.
Initializing
Just like the ways introduced in TanHaoQiang.
Subscripting a Multidimensional Array
If we supply fewer subscripts than there are dimensions, then the result is the inner-array element at the specified index 给定索引处的一个内层数组
:
// assigns the first element of arr to the last element in the last row of ia
ia[2][3] = arr[0][0][0];
int (&row)[4] = ia[1]; // binds row to the second four-element array in ia
Range for
Note
To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.
size_t cnt = 0;
for (auto &row : ia) // for every element in the outer array
for (auto &col : row) { // for every element in the inner array
col = cnt; // give this element the next value
++cnt; // increment cnt
}
We want to change the value of the elements, so we declare our control variables, row and col, as references. In the previous example, we used references as our loop control variables because we wanted to change the elements in the array. However, there is a deeper reason for using references. As an example, consider the following loop:
for (const auto &row : ia) // for every element in the outer array
for (auto col : row) // for every element in the inner array
cout << col << endl;
We still define the control variable of the outer loop as a reference. We do so in order to avoid the normal array to pointer conversion.Had we neglected the reference and written these loops as:
for (auto row : ia)
for (auto col : row)
our program would not compile. In this loop the type of row is int*
. The inner for
loop is illegal.
Pointers
Because a multidimensional array is really an array of arrays, the pointer type to which the array converts is a pointer to the first inner array:
int ia[3][4]; // array of size 3; each element is an array of ints of size 4
int (*p)[4] = ia; // p points to an array of four ints
p = &ia[2]; // p now points to the last element in ia
Note that the parentheses in this declaration are essential:
int *ip[4]; // array of pointers to int
int (*ip)[4]; // pointer to an array of four ints
With the advent of the new standard, we can often avoid having to write the type of a pointer into an array by using auto
or decltype
.
// print the value of each element in ia, with each inner array on its own line
// p points to an array of four ints
for (auto p = ia; p != ia + 3; ++p) {
// q points to the first element of an array of four ints; that is, q points to an int
for (auto q = *p; q != *p + 4; ++q)
cout << *q << ' ';
cout << endl;
}
The inner for
loop prints the values of the inner arrays. It starts by making q
point to the first element in the array to which p
points. The result of *p
is an array of four ints
. As usual, when we use an array, it is converted automatically to a pointer to its first element. The inner for
loop runs until we’ve processed every element in the inner array. To obtain a pointer just off the end of the inner array, we again ereference p
to get a pointer to the first element in that array. We then add 4 to that pointer to process the four elements in each inner array.
Of course, we can even more easily write this loop using the library begin
and end
functions.
// p points to the first array in ia
for (auto p = begin(ia); p != end(ia); ++p) {
// q points to the first element in an inner array
for (auto q = begin(*p); q != end(*p); ++q)
cout << *q << ' '; // prints the int value to which q points
cout << endl;
}
Type Aliases Simplify Pointers to Multidimensional Arrays
A type alias 类型别名
can make it easier to read, write, and understand pointers to multidimensional arrays.
using int_array = int[4]; // new style type alias declaration
typedef int int_array[4]; // equivalent typedef declaration
// print the value of each element in ia, with each inner array on its own line
for (int_array *p = ia; p != ia + 3; ++p) {
for (int *q = *p; q != *p + 4; ++q)
cout << *q << ' ';
cout << endl;
}
Here we start by defining int_array
as a name for the type “array of four ints.” We use that type name to define our loop control variable in the outer for loop.
施工中 -2019.1.22
string
学习完成 -2019.1.22
vector 学习完成 -2019.1.23
iterator 学习完成 -2019.1.23
Array 学习完成 -2019.1.23
Multidimensional Array 学习完成 -2019.1.25
施工完成 -2019.1.25