一、控制内存分配
-
new表达式与operator new函数
string *sp = new string("hello world");//分配并初始化一个string对象 string *arr = new string[10];//分配10个默认初始化的string对象 delete sp;//销毁*sp,然后释放sp指向额内存空间 delete[] arr;//销毁数组中的元素,然后释放对应的内存空间
当我们使用一条new表达式时:
实际执行了三步操作.第一步,new表达式调用一个operator new(或者operator new[])的标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组)。第二步,编译器运行相应的构造函数以构造这些对象,并为其传入初始值。第三步,对象被分配了空间并构造完成,返回一个指向该对象的指针。
当我们使用一条delete表达式删除一个动态分配的对象时:
实际执行了两步操作。第一步,对sp所指的对象或者arr所指的数组中的元素执行对应的析构函数。第二步,编译器调用名为 operator delete(或者operator delete[])的标准库函数释放内存空间。
如果程序希望控制内存分配的过程, 则它们需要定义自己的operator new函数和operator delete函数。即使标准库中已经存在这两个函数的定义,我们依旧可以定义自己的版本。
一条new表达式的执行过程总是先调用operator new函数以获取内存空间,然后在得到的内存空间中构造对象。与之相反,一个delete表达式的执行过程总是先销毁对象,然后调用operator delete函数释放对象所占的空间。
-
malloc函数与free函数
malloc函数接受一个表示待分配字节数的size_t,返回指向分配空间的指针或者返回0以表示分配失败。free函数接受一个void*,它是malloc返回的指针的副本,free将相关内存返回给系统。调用free(0)没有任何意义。
编写operator new 和 operator delete的一种简单方式,其他版本与之类似:
void *operator new(size_t size) { if(void *mem = malloc(size)) { return mem; } else { throw bad_alloc(); } } void operator delete(void *mem) noexcept { free(mem); }
-
显示析构函数调用
string *sp = new string("hello world"); sp->~string();
调用析构函数会销毁对象,但是不会释放内存。
二、运行时类型识别
-
dynamic_cast运算符的使用形式:
dynamic_cast<type*>(e)
dynamic_cast<type&)(e)
dynamic_cast<type&&)(e)
第一种形式中,e必须是一个有效的指针;在第二种形式中,e必须是一个左值;在第三种形式中,e不能是左值。
e的条件:必须是目标type的共有派生类、e的类型是目标type的共有基类或者e的类型就是目标type的类型。
返回:如果符合,则转换成功。否则如果转换的是指针类型并且失败了,则结果为0,如果是引用类型并且失败了,则抛出一个bad_cast异常。
-
指针类型的dynamic_cast
if(Derived *dp = dynamic_cast<Derived*>(bp)) else
我们可以对一个空指针执行dynamic_cast,结果是所需类型的空指针。
-
引用类型的dynamic_cast
void f(const Base &b) { try { const Derived &b = dynamic_cast<const Derived&>(b); } catch(bad_cast) { } }
-
-
typeid运算符
它允许程序向表达式提问:你的对象是什么类型?
使用typeid运算符:
通常情况下,我们使用typeid比较两条表达式的类型是否相同,或者比较一条表达式的类型是否与指定类型相同:
Derived *dp = new Dericed; Base *bp = dp; if(typeid(*bp) == typeid(*dp)) { //指向同一类型的对象 } if(typeid(*bp) == typeid(Derived)) { //bp实际指向Derived对象 }
下面的检查永远不执行:bp的类型时指向Base的指针
if(typeid(bp) == typeid(Derived)) { }
当typeid作用于指针(而非指针所指的对象),返回的结果是该指针的静态编译时类型。如果p是一个空指针,则typeid(*p)将抛出一个名为bad_typeid的异常。
-
type_Info类
- t1 == t2如果对象t1和t2 表示相同类型,返回true,否则false
- t1 != t2 如果type_info对象t1和t2表示不同的类型,返回true,否则false
- t.name() 返回一个C风格字符串,表示类型名字的可打印形式。
- t1.before(t2) 返回一个bool值,表示t1是否位于t2之前。
int arr[10]; Derived d; Base *p = &d; cout << typeid(42).name() << endl; cout << typeid(arr).name() << endl; cout << typeid(Sales_data).name() << endl; cout << typeid(std::string).name() << endl; cout << typeid(p).name() << endl; cout << typeid(*p).name() << endl;
结果如下:i,A10_i,10Sales_data,Ss,P4Base,7Derived
三、枚举类型
-
(非)限定作用域枚举类型
#include<bits/stdc++.h> using namespace std; enum class MyEnum { p1 = 1, p2 }; int main() { MyEnum myEnum = MyEnum::p1; cout << (int)myEnum << endl;//一定要加int }
没有class就是不限定作用域
#include<bits/stdc++.h> using namespace std; enum class MyEnum { p1 = 1, p2 }; int main() { enum color { p1 = 1, p2 }; color p3 = p2; cout << p3 << endl; }
-
枚举类型的前置声明
enum intV : unsigned long long;//不限定作用域的前置声明 enum class open_modes; //默认int
四、类成员指针
首先定义一个Screen类
#include<bits/stdc++.h>
using namespace std;
class Screen
{
public:
Screen(string str)
{
contents = str;
}
void f1()
{
cout << "1" << endl;
}
static const string Screen::*data()
{
return &Screen::contents;
}
public:
std::string contents;
};
-
数据成员指针
Screen sc("hello world!"); auto pdata = &Screen::contents;//pdata声明成一个"指向Screen类的const string成员的指针”只能读取它所指的成员,而不能向它写入内容 cout << sc.*pdata << endl;
-
返回数据成员指针的函数
Screen sc("hello world!"); const string Screen::*d = Screen::data(); auto s = sc.*d;//要想使用pdata,必须把它绑定到Screen类型的对象上 cout << s << endl;
-
成员函数指针
Screen sc("hello world!"); auto pmf = &Screen::f1; (sc.*pmf)();//括号必不可少!
-
成员指针函数表
#include<bits/stdc++.h> using namespace std; class Screen { public: Screen(string str) { contents = str; } void f1() { cout << "1" << endl; } static const string Screen::*data() { return &Screen::contents; } Screen& home(); Screen& forward(); Screen& back(); Screen& up(); Screen& down(); public: using Action = Screen& (Screen::*)(); enum Directions { Home, FORWARD, BACK, UP, DOWN }; std::string contents; Screen& Screen::move(Directions cm) { return (this->*Menu[cm])(); } public: static Action Menu[]; //函数表 }; int main() { Screen myScreen("hello world!"); myScreen.move(Screen::Home); myScreen.move(Screen::DOWN); }
初始化函数表:
Screen::Action Screen::Menu[] = { &Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down }
-
将成员函数用作可调用对象
-
使用mem_fn生成一个可调用对象
vector<string> vec; vec.push_back(""); cout << count_if(vec.begin(),vec.end(),mem_fn(&string::empty)) << endl; //1
-
五、union
-
union是一种特殊的类。一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当我们给union的某个成员赋值之后,该union的而其他成员就变成未定义的状态了。分配给一个union对象的存储空间至少要能容纳它的最大的数据成员。
#include<bits/stdc++.h> using namespace std; union Token { char cval; int ival; double dval; }; int main() { Token fir = {'a'}; // Token sec; // Token *pt = new Token; cout << fir.cval << endl; fir.ival = 10; cout << fir.ival << endl; cout << fir.cval << endl;//木有了 }
-
union可以为其成员指定public、protected和private等保护标记。默认情况下,union的成员都是共有的,这一点与struct相同。
-
union可以定义包括构造函数和析构函数在内的成员函数。但是由于union既不能继承自其他类,也不能作为基类使用,所以在union中不能含有虚函数。
-
匿名union,可直接访问它的成员
#include<bits/stdc++.h> using namespace std; int main() { union { char cval; int ival; double dval; }; cval = 'c'; ival = 42; cout << cval << endl; cout << ival << endl; }