随着自己学习C++11的进度,今天记录和实战C++11的战果。废话少说,直接记录C++11的点滴。
在前面学习系列里面,介绍了模板容器类vector,是一个单链表。今天来了解一下C++中的数组。数组也是存放相同类型的容器,数组的大小是固定不变的(编译时数组的维度必须是已知的)。如果想动态操作容器(增加,删除等)或者事先不知道容器的大小,请使用vector。 在使用数组时注意一下几点: 1.数组的维度必须是常量表达式,在编译时是已知的。
#错误的,无法通过非常量表达式初始化数组,请使用constexpr.
unsigned int size = 12;
#正确的
constexpr unsigned int size = 12;
#getsize()返回结果必须是常量表达式。
int a[size/getsize()]={1,2,3};
2.数组的类型不能使用auto关键字推断类型。 3.显示初始化数组元素时,可省略数组的维度。
int a[]={1,2,3};
string b[4]={"a","b"};
4.数组时不允许copy和赋值,不能将数组的内容拷贝给其他数组作为初始值,也不能为其他数组赋值。
int a[]={1,2,3};
// 错误的
int b[] = a;
// 错误的
b=a;
5.指针和数组很多细节需要注意,包括通过指针访问数组元素。比如:
// temp是含有10个整形指针的数组
int *temp[10];
// temp是个指针,指向一个含有10个整数的数组。
int (*temp)[10];
// temp引用一个含有10个整数的数组。
int (&temp)[10];
string nums[]={"a","b"};
// 指向数组的首元素的指针
string *p = nums;
// p1的类型是指针类型,指向的是string*
auto(nums) p1;
// p2 是一个字符串数组
decltype(nums) p2 = {"c","d"};
//C++11引入新的标准,来获得数组的首元素地址和末尾元素的下一个位置的地址。
int nums [] = {2,3,4,5};
int *start = begin(nums);
int *last = end(nums);
强制类型显示转换有static_cast,dynamic_cast,const_cast和reinterpret_cast.其中dynamic_cast是运行时识别的一种技术,后面会详细介绍。
再详细讲解C++11函数对象之前,首先要熟悉看一下局部对象,局部对象包括:自动对象和局部静态对象。
局部对象 形参和函数体内定义的变量,局部变量还会隐藏外层作用域的同名对象。在所有函数体之外定义的对象存在于程序的整个执行过程,当程序启动时被创建,知道程序结束时被创建。
依据形参传递的类型将函数传递,分为按引用传递和按值传递,当形参为引用类型时是按引用传递,实际是传递实参的别名。当实参的值被拷贝给形参时,形参和实参是两个互相独立的对象,这是按值传递。还有一种是指针形参,指针是按值传递的,当执行指针拷贝时,拷贝的是指针的值。所以你可以通过指针来修改实参的值。 由于拷贝大的类型对象或者容器对象比较低效,甚至有的类型(IO类型)是不支持拷贝的,这时我们尽量采用按引用传递,这样可以避免拷贝付出的代价。如果函数内无须改变参数的值时,最好将其声明为常量引用。
顶层const作用于对象本身。
// 顶层const对象,不能改变a
const int a = 12;
// 当拷贝a时,忽略了顶层const的影响。
int i = a;
// 顶层const对象,不能改变p的值。
int * const p = &i;
// 但可以通过p所指向的地址修改它的value。
*p=12;
当用实参初始化形参时会忽略掉顶层const,形参的顶层const会被忽略掉。
//func既可以接收int,也可以接收const int,但是不能修改i的值。
void func(const int i);
尽量使用常量引用
可变形参是通过initializer_list实现,但是要求类型一致。
#include <iostream>
void message(std::initializer_list<std::string> temp){
for(auto i:temp){
std::cout << i << std::endl;
}
}
int main() {
message({"hello","world","brian"});
return 0;
}
1.不要返回局部对象的引用或者指针 由于局部对象在函数调用完后,局部对象所占用的存储空间随之被释放。 2.引用返回左值 调用一个返回引用的函数得到左值,其它返回类型为右值。
#include <iostream>
char &get_char(std::string & a,std::string::size_type index) {
return a[index];
}
int main() {
std::string s="hello";
get_char(s,2)='E';
std::cout <<s<< std::endl;
return 0;
}
# 输出为
hEllo
3.通过列表初始化返回多个值
#include <iostream>
#include <vector>
std::vector<std::string> get_err(){
return {"hello","error"};
}
int main() {
auto list = get_err();
for(auto i : list){
std::cout << i << std::endl;
}
return 0;
}
#输出为
hello
error
4.返回数组的指针 由于数组不能被拷贝,所以函数不能返回数组,但是可以返回数组的指针或引用。 在C语言中通过typedef来重命名一个类型名称,在C++中通过using来,虽然typedef可以在c++继续使用,但是还是建议使用using.
typedef int arr[10]; //arr是类型别名,表示含有10个整数的数组。
等价于
using arr = int[10];
int (*p2)[10] = &arr; //p2是一个指针指向一个含有10个整数的数组
值得注意的是如果要返回数组指针的函数,稍微麻烦一些: Type(*function(parameter_list))[dimension] int(*func(int i))[10],func是一个函数指针,指向一个返回值为10个整数的数组。 由于这样声明太过于麻烦,所以在c++11标准中通过尾置返回类型,任何函数都能使用尾置返回类型。上述的声明可以通过: auto func(int i)->int(*)[10]