c++noexcept的简单介绍
本篇文章给大家谈谈c++noexcept,以及对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
c++中 string 类的find函数的用法
string类的查找函数:
int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位芦枣置
int find(const char *s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查找字符串s中前n个字符在当前串中的位置
int find(const string s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
//查找成功时返回所在位置,失败返回string::npos的值
int rfind(char c, int pos = npos) const;//从pos开始从后向前查找字符c在当前串中的位置
int rfind(const char *s, int pos = npos) const;
int rfind(const char *s, int pos, int n = npos) const;
int rfind(const string s,int pos = npos) const;
//从pos开始从后向前查找字符串s中前n个字符组成的字符串在当前串中的位置,成功返回所在位置,失败时返迟空回string::npos的值
int find_first_of(char c, int pos = 0) const;//从pos开始查找字符c第一次出现的位置
int find_first_of(const char *s, int pos = 0) const;
int find_first_of(const char *s, int pos, int n) const;
int find_first_of(const string s,int pos = 0) const;
//从pos开始查找当陪旦拆前串中第一个在s的前n个字符组成的数组里的字符的位置。查找失败返回string::npos
int find_first_not_of(char c, int pos = 0) const;
int find_first_not_of(const char *s, int pos = 0) const;
int find_first_not_of(const char *s, int pos,int n) const;
int find_first_not_of(const string s,int pos = 0) const;
//从当前串中查找第一个不在串s中的字符出现的位置,失败返回string::npos
int find_last_of(char c, int pos = npos) const;
int find_last_of(const char *s, int pos = npos) const;
int find_last_of(const char *s, int pos, int n = npos) const;
int find_last_of(const string s,int pos = npos) const;
int find_last_not_of(char c, int pos = npos) const;
int find_last_not_of(const char *s, int pos = npos) const;
int find_last_not_of(const char *s, int pos, int n) const;
int find_last_not_of(const string s,int pos = npos) const;
//find_last_of和find_last_not_of与find_first_of和find_first_not_of相似,只不过是从后向前查找
c++new的用法
c++new的用法
1、new operator
这个就是平时最经常用的new,用法如下程序所示:
1 class A
2 {
3 public:
4 A(int i) :a(i){}
5 private:
6 int a;
7 };
8
9 int main()
10 {
11 A* example = new A(1);
12 }
new operator实际上执行了以下三个步骤:
1、调用operator new分配内存(后面要说的第二种new),如果类本身定义了operator new,那么会调用类自己的operator new,而不是全局的;
2、调用A的构造函数A::A(int);
3、返回相应的指针
2、operator new
operator new不调用构造函数,而仅仅分配内存,有两个版本,前者抛出异常,后者当失败时不抛出异常,而是直接返回:
void* operator new (std::size_t size);
void* operator new (std::size_t size, const std::nothrow_t nothrow_value) noexcept;
可以看到,operator new的作用有点类似与C语言中的malloc,有的地方说operator new的底租卜层实现可以是malloc。
C++中可以用set_new_handler()设置抛出bad_alloc()异常时调用的处理函数,Effective C++有几个条款很详细描述了具体做法。
1 class A
2 {
3 public:
4 A(int i) :a(i){}
5 void* operator new(size_t size)
6 {
7 cout "call A::operator new" endl;
8 return malloc(size);
9 }
10 void operator delete(void* p)
11 {
12 cout "call A::operator delete" endl;
13 return free(p);
14 }
15 void* operator new(size_t size, const nothrow_t nothrow_value) noexcept
16 {
17 cout "call A::operator new (noexcept)" endl;
18 return malloc(size);
19 }
20 void operator delete(void* p, const nothrow_t nothrow_value) noexcept
21 弊洞穗 {
22 cout "call A::operator delete (noexcept)" endl;
23 free(p);
24 }
25 private:
26 颤码int a;
27 };
28
29 int main()
30 {
31 A* example1 = new A(1);
32 delete example1;
33 A* example2 = new(nothrow) A(2);
34 delete example2;
35 }
用一个小例子可以证明一下,确实调用的是自定义operator new/delete,而不是全局的。
3、placement new
placement new仅在一个已经分配好的内存指针上调用构造函数,基本形式如下:
void* operator new (std::size_t size, void* ptr) noexcept;
placement new不需要抛出异常,因为它自身不分配内存。
[img]c++中如何实现靠0原则
你们中的一些人可能已经知道零度规则。你们中的更多人可能听说过3号规则(前C ++ 11)或5号规则。让我们首先回顾一下“零规则”是什么。
什么是零规则?
它背后的想法如下:类不应定义任何特殊函数(复制/移动构造函数/赋值和析构函数),除非它们是专用于资源管理的类。它在此描述博客文章。这有几个很好的理由,一个更具概念性,另一个更实用。概念上的原因源于单一责任原则,即一个阶级应该对一件事负责的想法。如果你让一个类负责多个事情,那么你已经将两个独立事物的实现和接口紧密耦合在一起。你的类的工作是协调其成员变量的状态,以提供一些组合状态,以及与该状态的接口。当你编写特殊的成员函数时,你基本上就会收集那些没有以你想要的方式管理资源的成员留下的东西。正确的做法是确保您使用正确的成员。这直接导致了我们的实际原因:在c ++中,一个类不能选择它将重新定义特殊功能的成员。我的意思是什么?好吧,如果你不编写特殊函数,编译器会通过尝试逐个成员地应用预期的操作来为你生成它们。因此,如果使用默认的移动构造函数,编译器将生成一个移动构造函数,它只是尝试移动构造所有数据成员(和基类)。只要编写一个自定义移动构造函数,对一个数据成员进行一些特殊处理,您就必须编写代码来处理其他所有成员,即使您只需要更改一个。毕竟,根据自定义特殊功能,每个变量的一行代码是什么如高卜似乎不是什么大问题?真正的问题不是必须编写它,因为编译器不会强制执行它并且很容易忘记。
span style="color:#555555"class Example { public: Example(const Example other) : m_double(other.m_double) { } private: double m_double = 0.0; bool m_bool = false; }; /span
您不会在IDE或任何清理程序中收到此警告,因为m_bool将通过其内联初始化确定性地设置为false,并且在构造函数初始化列表中未提及它。但这几乎肯定是一个错误,因为无论何时复制一个Example对象,无论副本的来源如何,新副本都将具有m_bool = false。像这样的东西很容易引入,之后可能很难挖掘。
为什么人们违反规则
当然,人们通常不会像上面的例子那样编写代码,因为双精度和布尔值很简单,并且几乎可以保证你对它们的默认资源语义感到满意。让我们来看一个非常常见的真实念缓案例:指针。一个类有指针是很常见的。当然,我们是精明的现代c ++ 11程序员,所以我们将使用unique_ptr,防止任何可能的泄漏。问题是,尽管unique_ptr提供了正确的析构函数,但除非我们希望拥有unique_ptr的类是不可复制的,否则它不会执行我们想要的复制构造函数/赋值。让我们假设Example想要通过指针拥有一个非多态类Pointee,我们最终得到的代码如下所示:
span style="color:#555555"class Example { public: Example(const Example other) : m_pointer(make_uniquePointee(*other.m_pointer)) , m_bool(other.m_bool) { } // Won't be default generated unless you add this Example(Example ) = default; // similar code for copy/move assignment private: unique_ptrPointee m_pointer; bool m_bool = false; }; /span
沿着这些方向的代码在野外很常见,并且它不是最佳的。在更大的类中,添加或删除变量的每个修改都很容易在复制/移动语义中引入错误。我们可以做得更好吗?
如何遵守规则
上面代码的结论是unique_ptr实际上并不代表我们类的正确渣穗资源管理。因此,我们不应该将这个问题作为整个Example类的问题,包括其他所有成员,而应该在根目录中解决问题。让我们编写一个具有适当资源语义的类。
span style="color:#555555"templateclass T class DeepCopyPointer { public: DeepCopyPointer(const DeepCopyPointer other) : m_pointer(make_uniqueT(*other.m_pointer)) { } DeepCopyPointer(DeepCopyPointer ) = default; // similar code for copy/move assignment private: unique_ptrT m_pointer; }; /span
这看起来更好,对吧?嗯,有一个问题:这个类实际上并没有提供unique_ptr所做的任何接口。我们可以让Example成为朋友并让它直接访问unique_ptr,但这很难看。另一个问题是这个代码非常专业,它只适用于unique_ptr,它只适用于非多态对象:如果我们存储基类指针并且对象的类型不同,则此副本将无法正常工作。我们可能想要通过调用克隆方法或类似方法进行复制。
打破另一条规则是更好的解决方案
让我们做一些更通用的事情。让我们编写一个模仿另一个类的类,但允许您更改其复制行为。为了模仿其他类,我们将继承它。现在,我们有兴趣模仿unique_ptr,继承它是不是很难?人们经常说你不应该继承像unique_ptr或vector这样的类型,因为它们没有虚函数,特别是析构函数。答案是,只要你从不以多态方式使用它,继承就不错了。考虑到这一点:
span style="color:#555555"template class T, class F class copyer : public T { public: copyer(T t) : T(std::forward(t)) { }; copyer(copyer other) = default; copyer operator=(copyer other) = default; copyer(const copyer other) : T(F()(other)) { } copyer operator=(const copyer other) { std::swap(*this, copyer(other)); return *this; } }; /span
T是要模仿的类,F是一个有助于我们重新定义复制行为的函子。请注意,我们将使用移动构造/赋值的默认值,我们假设我们喜欢T的移动构造/赋值的默认值。我选择这样做是因为如果T不可移动(或者不能以我们喜欢的方式移动),那么一般来说编写这段代码会相当棘手。要获得我们想要的深层副本unique_ptr,我们只需执行以下操作:
span style="color:#555555"template class T struct F { std::unique_ptrT operator()(const std::unique_ptrT other) { return std::make_uniqueT(*other); } }; template class T using copying_ptr = copyerstd::unique_ptrT, FT; /span
我们现在可以像这样编写示例:
span style="color:#555555"class Example { private: copying_ptrPointee m_pointer; bool m_bool = false; }; /span
就是这样,我们现在免费获得所有特殊功能。我们不必担心每次添加/删除变量时都会向Example添加错误。我们也可以轻松地重用copyer,如果Example想要以不同的方式改变复制语义,它可以只定义一个私有嵌套结构并在模板复制器中使用它而不是F.
综上所述
不要为复杂的类开始编写大的特殊函数,只是因为有几个成员没有正确的默认行为。使用像这样的技术来代替具有默认行为的成员; 您的代码将更清晰,更不容易出错。编辑:这是关于异常安全的简要跟进,受到Ross Smith(CaptainCrowbar)对cpp reddit的一些优秀评论的启发。
强有力的例外保证
有一个地方默认生成的特殊功能不足。强大的异常保证说如果函数通过抛出失败,那么程序的状态与调用函数之前的状态相同。对于构造函数,如果每个成员都有适当的析构函数,则会自动维护; 构造函数体中抛出的异常意味着将销毁部分构造的对象。移动分配是移动,对于具有noexcept移动操作的类型来说,它是非常常见和可取的。所以复制任务是奇怪的人。默认的复制分配将尝试按顺序复制分配每个成员。例如:
span style="color:#555555"class Example { private: std::vector m_vec1; std::vector m_vec2; }; Example e1; Example e2; .... e2 = e1; /span
这里可能发生的是e1.m_vec1将被复制到e2.m_vec1中,然后将e1.m_vec2的副本复制到e2.m_vec2中。现在,e2处于半复制状态,强大的异常保证被破坏。所以这是我们如何解决它:
span style="color:#555555"class Example { public: Example(const Example ) = default; Example(Example ) = default; Example operator=(Example ) = default; Example operator=(const Example other ) { std::swap(*this, Example(other)); return *this; } private: std::vector m_vec1; std::vector m_vec2; }; /span
我只是使用std :: swap而不是using namespace std技术,因为我知道我不会写自定义交换; 在绝大多数情况下,没有必要。现在,如果抛出异常,它将在执行交换之前在Example(其他)中构造临时值时抛出,并且e2将保持不变。请注意,std :: swap调用移动构造函数并移动赋值,因此三个默认的特殊函数都用于生成提供强保证的复制赋值。那么,这是否符合零规则的精神?它似乎不是,因为我们明确写了一个特殊的功能。但我认为确实如此。我强调的主要是让你的班级处理其成员的资源管理细节是不可取的。在这里,这不会发生。请注意,添加或删除成员时,不需要更改任何特殊功能代码,前提是它们是提供正确语义的功能代码。也许拿走就是这样:Zero规则中的真正零点是你的特殊函数应该引用你的成员变量的零。无论是源于不写入,明确默认,还是复制和交换,所有这些方法都很好。
C++中的virtual关键字是什么?怎么用?
virtual是定义C++中虚函数的关键字 。
1、virtual关键字的作用:
c++中的函数调用默认不适用动态绑定。要触发动态绑定,必须满足两个条件:第一,指定为虚函数;第二,通过基类类型的引用或指针调用。由此可见,virtual主要功能是实现动态绑定。
2、virtual关键字的使用情况:
virtual可用来定义类函数和应用到虚继承。
友元函数 构造函数 static静态函数 不能用virtual关键字修饰;
普通明斗成员函数 和析构函数 可以用virtual关键字修饰。
3、virtual关键字的效果:
class GrandFather //祖父类
{
public:
GrandFather() {} //构造函数
virtual void fun() //虚函数声明定义
{
cout "GrandFather call function!" endl;
}
};
class Father : public GrandFather//父类,公有继承祖父类
{
public:
Father() {} //构造函数
void fun() //fun函数声明定义
{
cout "Father call function!" endl;
}
};
class Son : public Father //子类,仔槐猛公有继承父类
{
public:
Son() {} //构造函数
void fun() //fun函数声明定义
{
cout "Son call function!" endl;
}
};
void print(GrandFather* father) //输出函数 ,祖父类形参
{
father-fun(); //调用fun函数
}
int _tmain(int argc, _TCHAR* argv[])
{
Father * pfather = new Son;//建立一个父类的指针让它指向子类
pfather-fun();
GrandFather * pgfather = new Father;
print(pgfather); //祖父类指针变量
return 0; }
4、virtual的继承性:
只要基函数定义了virtual,继承类的该函数也就具有virtual属性;即 GrandFather, Father ,Son同时定义virtual void fun()与GrandFather一个定义virtual void fun效果是一样的。
扩展资料
vitual关键字的用途:
1、vitual基类
在多重继承中,从派生类到基类存在多条路线时(多个继承脉络或者途径),一个这种派生类的对象实例化将包含多个基类对象,浪费资源且调用混乱的现象出现。
因此引入了vitual baseclass,来在运行阶段克服多个基类对象的产生。这个vitual是在运行阶段保证对象唯一性的。
2.vitual函数
虚函数的出现,是由于基类的指针可以执行派生类,因此引出了不便,引入vitual函数,来告诉编译器,出现这种情况时,在运行时动态链接进行处理。
3.vitual在纯虚函数中使用
纯虚函数完全是为了继承体系的完整,也是集成vitual函数的作用而产生的。代表了编译器阶段对象的绑定,将调用选择转移到运行时动态绑定。
综上:vitual关键的引入,可以理解为念桥阻止编译阶段的静态绑定,将绑定(虚函数)和约束工作(虚基类)转移到运行时动态处理。
参考资料:百度百科——virtual
关于c++noexcept和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。