说说 C++ 中智能指针和指针的区别是什么
参考回答
- 智能指针
如果在程序中使用new从堆(自由存储区)分配内存,等到不需要时,应使用delete将其释放。C++引用了智能指针auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL)表明,需要有更精致的机制。基于程序员的编程体验和BOOST库提供的解决方案,C++11摒弃了auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr和weak_ptr。所有新增的智能指针都能与STL容器和移动语义协同工作。 - 指针
C 语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。 - 智能指针和普通指针的区别
智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制,区别是它负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期
说说 C++中的智能指针有哪些?分别解决的问题以及区别
参考回答
- C++中的智能指针有4种,分别为:shared_ptr、unique_ptr、weak_ptr、auto_ptr,其中
auto_ptr被C++11弃用。 - 使用智能指针的原因
申请的空间(即new出来的空间),在使用结束时,需要delete掉,否则会形成内存碎片。在程序运行期间,new出来的对象,在析构函数中delete掉,但是这种方法不能解决所有问题,因为有时候new发生在某个全局函数里面,该方法会给程序员造成精神负担。此时,智能指针就派上了用场。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以,智能指针的作用原理就是在函数结束时自动释放内存空间,避免了手动释放内存空间。 - 四种指针分别解决的问题以及各自特性如下:
(1)auto_ptr(C++98的方案,C++11已经弃用)
采用所有权模式
auto_ptr<string> p1(new string("I reigned loney as a cloud."));
auto_ptr<string> p2;
p2=p1; //auto_ptr不会报错
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题。
(2)unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露,例如,以new创建对象后因为发生异常而忘记调用delete时的情形特别有用。
采用所有权模式,和上面例子一样
auto_ptr<string> p3(new string("I reigned loney as a cloud."));
auto_ptr<string> p4;
p4=p3; //此时不会报错
编译器认为P4=P3非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全。 另外unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); //#2 allowed
其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。
注意:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。例如:
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
(3)shared_ptr(非常好使)
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。
shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。
成员函数:
use_count返回引用计数的个数
unique 返回是否是独占所有权( use_count 为 1)
swap 交换两个 shared_ptr 对象(即交换所拥有的对象)
reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr
sp(new int(1)); sp 与 sp.get()是等价的
(4)weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete\n";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}
可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb; 运行结果如下,这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。
注意:我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb->print(); 英文pb是一个weak_ptr,应该先把它转化为shared_ptr,如:
shared_ptr p = pa->pb_.lock(); p->print();
简述 C++ 右值引用与转移语义
参考回答
- 右值引用
一般来说,不能取地址的表达式,就是右值引用,能取地址的,就是左值。
class A { };
A & r = A(); //error,A()是无名变量,是右值
A && r = A(); //ok,r是右值引用
- 转移语义
move 本意为 “移动”,但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。基于 move() 函数特殊的功能,其常用于实现移动语义。
简述 C++ 中智能指针的特点
参考回答
- C++中的智能指针有4种,分别为:shared_ptr、unique_ptr、weak_ptr、auto_ptr,其中auto_ptr被C++11弃用。
- 为什么要使用智能指针:智能指针的作用是管理一个指针,因为存在申请的空间在函数结束时忘记释放,造成内存泄漏的情况。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,自动释放资源。
- 四种指针各自特性
(1)auto_ptr
auto指针存在的问题是,两个智能指针同时指向一块内存,就会两次释放同一块资源,自然报错。
(2)unique_ptr
unique指针规定一个智能指针独占一块内存资源。当两个智能指针同时指向一块内存,编译报错。
实现原理:将拷贝构造函数和赋值拷贝构造函数申明为private或delete。不允许拷贝构造函数和赋值操作符,但是支持移动构造函数,通过std:move把一个对象指针变成右值之后可以移动给另一个unique_ptr
(3)shared_ptr
共享指针可以实现多个智能指针指向相同对象,该对象和其相关资源会在引用为0时被销毁释放。
实现原理:有一个引用计数的指针类型变量,专门用于引用计数,使用拷贝构造函数和赋值拷贝构造函数时,引用计数加1,当引用计数为0时,释放资源。
注意:weak_ptr、shared_ptr存在一个问题,当两个shared_ptr指针相互引用时,那么这两个指针的引用计数不会下降为0,资源得不到释放。因此引入weak_ptr,weak_ptr是弱引用,weak_ptr的构造和析构不会引起引用计数的增加或减少。
weak_ptr 能不能知道对象计数为 0,为什么
参考回答
不能。
weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象。进行该对象管理的是那个引用的shared_ptr。weak_ptr只是提供了对管理 对象的一个访问手段。weak_ptr设计的目的只是为了配合shared_ptr而引入的一种智能指针,配合shared_ptr工作,它只可以从一个shared_ptr或者另一个weak_ptr对象构造,它的构造和析构不会引起计数的增加或减少
weak_ptr 如何解决 shared_ptr 的循环引用问题
参考回答
为了解决循环引用导致的内存泄漏,引入了弱指针weak_ptr,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问
share_ptr 怎么知道跟它共享对象的指针释放了
参考回答
多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当无shared_ptr托管该指针时,delete该指针
说说智能指针及其实现,shared_ptr 线程安全性,原理
- C++中的智能指针有4种,分别为:shared_ptr、unique_ptr、weak_ptr、auto_ptr,其中auto_ptr被C++11弃用。
- 为什么要使用智能指针
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。 - 四种指针分别解决的问题以及各自特性如下:
(1)auto_ptr(C++98的方案,C++11已经弃用)
采用所有权模式
auto_ptr<string> p1(new string("I reigned loney as a cloud."));
auto_ptr<string> p2;
p2=p1; //auto_ptr不会报错
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题。
(2)unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露,例如,以new创建对象后因为发生异常而忘记调用delete时的情形特别有用。
采用所有权模式,和上面例子一样
auto_ptr<string> p3(new string("I reigned loney as a cloud."));
auto_ptr<string> p4;
p4=p3; //此时不会报错
编译器认为P4=P3非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全。 另外unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); //#2 allowed
其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。
注意:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。例如:
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
(3)shared_ptr(非常好使)
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。
shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。
成员函数:
use_count返回引用计数的个数
unique 返回是否是独占所有权( use_count 为 1)
swap 交换两个 shared_ptr 对象(即交换所拥有的对象)
reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr
sp(new int(1)); sp 与 sp.get()是等价的
(4)weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete\n";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}
可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb; 运行结果如下,这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。
注意:我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb->print(); 英文pb是一个weak_ptr,应该先把它转化为shared_ptr,如:
shared_ptr p = pa->pb_.lock(); p->print();
3. 线程安全性
多线程环境下,调用不同shared_ptr实例的成员函数是不需要额外的同步手段的,即使这些shared_ptr拥有的是同样的对象。但是如果多线程访问(有写操作)同一个shared_ptr,则需要同步,否则就会有race condition 发生。也可以使用 shared_ptr overloads of atomic functions来防止race condition的发生。
多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。例子如下:
//程序实例
shared_ptr<long> global_instance = make_shared<long>(0);
std::mutex g_i_mutex;
void thread_fcn()
{
//std::lock_guard<std::mutex> lock(g_i_mutex);
//shared_ptr<long> local = global_instance;
for(int i = 0; i < 100000000; i++)
{
*global_instance = *global_instance + 1;
//*local = *local + 1;
}
}
int main(int argc, char** argv)
{
thread thread1(thread_fcn);
thread thread2(thread_fcn);
thread1.join();
thread2.join();
cout << "*global_instance is " << *global_instance << endl;
return 0;
}
在线程函数thread_fcn的for循环中,2个线程同时对global_instance进行加1的操作。这就是典型的非线程安全的场景,最后的结果是未定的,运行结果如下:
*global_instance is 197240539
如果使用的是每个线程的局部shared_ptr对象local,因为这些local指向相同的对象,因此结果也是未定的,运行结果如下: *global_instance is 160285803
因此,这种情况下必须加锁,将thread_fcn中的第一行代码的注释去掉之后,不管是使用global_instance,还是使用local,得到的结果都是:
*global_instance is 200000000
请你回答一下智能指针有没有内存泄露的情况
参考回答
智能指针有内存泄露的情况发生。
- 智能指针发生内存泄露的情况
当两个对象同时使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄露。 - 智能指针的内存泄漏如何解决?
为了解决循环引用导致的内存泄漏,引入了弱指针weak_ptr,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。
//程序实例
#include <memory>
#include <iostream>
using namespace std;
class Child;
class Parent{
private:
std::shared_ptr<Child> ChildPtr;
public:
void setChild(std::shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
if (this->ChildPtr.use_count()) {
}
}
~Parent() {
}
};
class Child{
private:
std::shared_ptr<Parent> ParentPtr;
public:
void setPartent(std::shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
std::weak_ptr<Parent> wpp;
std::weak_ptr<Child> wpc;
{
std::shared_ptr<Parent> p(new Parent);
std::shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
std::cout << p.use_count() << std::endl;
std::cout << c.use_count() << std::endl;
}
std::cout << wpp.use_count() << std::endl;
std::cout << wpc.use_count() << std::endl;
return 0;
}
/*
程序运行结果:
2 2 1 1
*/
上述代码中,parent有一个shared_ptr类型的成员指向孩子,而child也有一个shared_ptr类型的成员指向父亲。然后在创建孩子和父亲对象时也使用了智能指针c和p,随后将c和p分别又赋值给child的智能指针成员parent和parent的智能指针成员child。从而形成了一个循环引用。
简述一下 C++11 中四种类型转换
参考回答
C++中四种类型转换分别为const_cast、static_cast、dynamic_cast、reinterpret_cast,四种转换功能分别如下:
- const_cast
将const变量转为非const - static_cast
最常用,可以用于各种隐式转换,比如非const转const,static_cast可以用于类向上转换,但向下转换能成功但是不安全。 - dynamic_cast
只能用于含有虚函数的类转换,用于类向上和向下转换
向上转换:指子类向基类转换。
向下转换:指基类向子类转换。
这两种转换,子类包含父类,当父类转换成子类时可能出现非法内存访问的问题。
dynamic_cast通过判断变量运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。dynamic_cast可以做类之间上下转换,转换的时候会进行类型检查,类型相等成功转换,类型不等转换失败。运用RTTI技术,RTTI是”Runtime Type Information”的缩写,意思是运行时类型信息,它提供了运行时确定对象类型的方法。在c++层面主要体现在dynamic_cast和typeid,vs中虚函数表的-1位置存放了指向type_info的指针,对于存在虚函数的类型,dynamic_cast和typeid都会去查询type_info。 - reinterpret_cast
reinterpret_cast可以做任何类型的转换,不过不对转换结果保证,容易出问题。
注意:为什么不用C的强制转换:C的强制转换表面上看起来功能强大什么都能转,但是转换不够明确,不能进行错误检查,容易出错
简述一下 C++ 11 中 auto 的具体用法
参考回答
auto用于定义变量,编译器可以自动判断变量的类型。auto主要有以下几种用法:
- auto的基本使用方法
(1)基本使用语法如下
auto name = value; //name 是变量的名字,value 是变量的初始值
注意:auto 仅仅是一个占位符,在编译器期间它会被真正的类型所替代。或者说,C++ 中的变量必须是有明确类型的,只是这个类型是由编译器自己推导出来的。
(2)程序实例如下
auto n = 10;
auto f = 12.8;
auto p = &n;
auto url = "www.123.com";
a. 第 1 行中,10 是一个整数,默认是 int 类型,所以推导出变量 n 的类型是 int。
b. 第 2 行中,12.8 是一个小数,默认是 double 类型,所以推导出变量 f 的类型是 double。
c. 第 3 行中,&n 的结果是一个 int* 类型的指针,所以推导出变量 f 的类型是 int*。
d. 第 4 行中,由双引号 “” 包围起来的字符串是 const char* 类型,所以推导出变量 url 的类型是 const char*,也即一个常量指针。
2. auto和 const 的结合使用
(1) auto 与 const 结合的用法
a. 当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
b. 当类型为引用时,auto 的推导结果将保留表达式的 const 属性。
(2)程序实例如下
int x = 0;
const auto n = x; //n 为 const int ,auto 被推导为 int
auto f = n; //f 为 const int,auto 被推导为 int(const 属性被抛弃)
const auto &r1 = x; //r1 为 const int& 类型,auto 被推导为 int
auto &r2 = r1; //r1 为 const int& 类型,auto 被推导为 const int 类型
a. 第 2 行代码中,n 为 const int,auto 被推导为 int。
b. 第 3 行代码中,n 为 const int 类型,但是 auto 却被推导为 int 类型,这说明当 = 右边的表达式带有 const 属性时,auto 不会 使用 const 属性,而是直接推导出 non-const 类型。
c. 第 4 行代码中,auto 被推导为 int 类型,这个很容易理解,不再赘述。
d. 第 5 行代码中,r1 是 const int & 类型,auto 也被推导为 const int 类型,这说明当 const 和引用结合时,auto 的推导将保留表达式的 const 类型。
3. 使用auto定义迭代器
在使用 stl 容器的时候,需要使用迭代器来遍历容器里面的元素;不同容器的迭代器有不同的类型,在定义迭代器时必须指明。而迭代器的类型有时候比较复杂,请看下面的例子:
#include <vector>
using namespace std;
int main(){
vector< vector<int> > v;
//vector< vector<int> >::iterator i = v.begin();
auto i = v.begin(); //使用 auto 代替具体的类型,该句比上一句简洁,根据表达式
v.begin() 的类型(begin() 函数的返回值类型)来推导出变量i的类型
return 0;
}
- 用于泛型编程
auto 的另一个应用就是当我们不知道变量是什么类型,或者不希望指明具体类型的时候,比如泛型编程中。请看下面例子:
#include <iostream>
using namespace std;
class A{
public:
static int get(void){
return 100;
}
};
class B{
public:
static const char* get(void){
return "www.123.com";
}
};
template <typename T>
void func(void){
auto val = T::get();
cout << val << endl;
}
int main(void){
func<A>();
func<B>();
return 0;
}
/*
运行结果:
100
www.123.com
*/
本例中的模板函数 func() 会调用所有类的静态函数 get(),并对它的返回值做统一处理,但是 get() 的返回值类型并不一样,而且不能自动转换。这种要求在以前的 C++ 版本中实现起来非常的麻烦,需要额外增加一个模板参数,并在调用时手动给该模板参数赋值,用以指明变量 val 的类型。但是有了 auto类型自动推导,编译器就根据 get() 的返回值自己推导出 val 变量的类型,就不用再增加一个模板参数了
简述一下 C++11 中的可变参数模板新特性
参考回答
可变参数模板(variadic template)使得编程者能够创建这样的模板函数和模板类,即可接受可变数量的参数。例如要编写一个函数,它可接受任意数量的参数,参数的类型只需是cout能显示的即可,并将参数显示为用逗号分隔的列表。
int n = 14;
double x = 2.71828;
std::string mr = "Mr.String objects!";
show_list(n, x);
show_list(x*x, '!', 7, mr); //这里的目标是定义show_list()
/*
运行结果:
14, 2.71828
7.38905, !, 7, Mr.String objects!
*/
要创建可变参数模板,需要理解几个要点:
(1)模板参数包(parameter pack);
(2)函数参数包;
(3)展开(unpack)参数包;
(4)递归。
简述一下 C++11 中 Lambda 新特性
参考回答
- 定义
lambda 匿名函数很简单,可以套用如下的语法格式:
[外部变量访问方式说明符] (参数) mutable noexcept/throw() -> 返回值类型
{
函数体;
};
其中各部分的含义分别为:
a. [外部变量方位方式说明符]
[ ]
方括号用于向编译器表明当前是一个 lambda 表达式,其不能被省略。在方括号内部,可以注明当前 lambda 函数的函数体中可以使用哪些“外部变量”。
所谓外部变量,指的是和当前 lambda 表达式位于同一作用域内的所有局部变量。
b. (参数)
和普通函数的定义一样,lambda 匿名函数也可以接收外部传递的多个参数。和普通函数不同的是,如果不需要传递参数,可以连同 () 小括号一起省略;
c. mutable
此关键字可以省略,如果使用则之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,对于以值传递方式引入的外部变量,不允许在 lambda 表达式内部修改它们的值(可以理解为这部分变量都是 const 常量)。而如果想修改它们,就必须使用 mutable 关键字。
注意:对于以值传递方式引入的外部变量,lambda 表达式修改的是拷贝的那一份,并不会修改
真正的外部变量;
d. noexcept/throw()
可以省略,如果使用,在之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,
lambda 函数的函数体中可以抛出任何类型的异常。而标注 noexcept 关键字,则表示函数体内不会抛出任何异常;使用 throw() 可以指定 lambda 函数内部可以抛出的异常类型。
e. -> 返回值类型
指明 lambda 匿名函数的返回值类型。值得一提的是,如果 lambda 函数体内只有一个 return
语句,或者该函数返回 void,则编译器可以自行推断出返回值类型,此情况下可以直接省略"-> 返回值类型"。
f. 函数体
和普通函数一样,lambda 匿名函数包含的内部代码都放置在函数体中。该函数体内除了可以使用指定传递进来的参数之外,还可以使用指定的外部变量以及全局范围内的所有全局变量。
2. 程序实例
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int num[4] = {4, 2, 3, 1};
//对 a 数组中的元素进行排序
sort(num, num+4, [=](int x, int y) -> bool{ return x < y; } );
for(int n : num){
cout << n << " ";
}
return 0;
}
/*
程序运行结果:
1 2 3 4
*/