CppPrimer笔记 Chapter19 特殊工具与技术
标签: Cpp
控制内存分配(19.1)
- 可以自定义
operator new
但不能改变new 运算符
- 对象的new与delete为隐式静态的的,而且不能操纵类的任何数据成员
- 调用
void *operator new(size_t)
传入对象字节数.
调用void *operator new[](size_t)
传入数组中所有元素所需空间 定位new
如下,不分配任何内存,只是单纯返回指针实参并由new表达式在指定的地址初始化对象
new(place_add)type[size]{braced initializer list
- 调用析构函数会销毁对象,但不会释放内存
明确 构造,析构 销毁内存,释放内存 之间的区别 可查找 RAII机制
运行时类型识别(RTTI)(19.2)
- 由
typeid
(返回表达式类型)与dynamic_cast
(将基类指针或引用安全地转换成派生类指针或引用)实现
特别适用于:用基类指针或引用执行某个非虚函数的派生类操作. - dynamic_cast使用:
dynamic_cast<type*>(e)
e必须为有效指针dynamic_cast<type&>(e)
e必须为左值dynamic_cast<type&&>(e)
e必不能为左值
只允许:
- e是type的公有派生类(向上转换)
- e是type的公有基类(向下转换,需要指向派生类的基类指针)
- e与type相同
这三种转换.
- 若指针转换失败,则是0,引用转换失败,抛出异常
bad_cast
typeid
用于返回表达式的类型.那么可以在发生了指针上转型等过程中判断指向的对象是否相同
dp = new Derived;
Base*bp = dp;
if (typeid(*bp) == typeid(*dp))
{
cout << "bp and dp point to same obj" << endl;
}
else
{
cout << "not same obj" << endl;
}
- 利用RTTI实现继承成体系中
==
由于使用虚继承体系,基类引用无法访问派生类中特有的对象
class Base{
friend bool operator==(const Base&, const Base&);
public:
int b;
protected:
virtual bool equal(const Base&)const;
};
bool Base::equal(const Base &rhs) const
{
return this->b == rhs.b;
}
class Derived :
public Base{
public:
string d;
protected:
bool equal(const Base&)const override;
};
bool Derived::equal(const Base &rhs) const
{
auto r = dynamic_cast<const Derived&>(rhs);
return this->b == r.b && this->d == r.d;
}
bool operator==(const Base&lhs, const Base&rhs)
{
//类型不同则返回false,否则调用equal
return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}
type_info
类仅能通过typeid
运算符创建,用于对象类型的比较等.
void demo_type_info()
{
int arr[10];
Derived d;
Base*p = &d;
cout << typeid (42).name() << endl
<< typeid (arr).name() << endl
<< typeid (string).name() << endl
<< typeid (p).name() << endl
<< typeid(*p).name()<<endl;
}
枚举类型(19.3)
enum Tokens{INLINE = 128,VIRTUAL = 129};
//Tokens这样的全局枚举,会自动提升为int,注释第一个函数也不会报错
void ff(Tokens){ cout << "ff_Tokens" << endl; }
void ff(int){ cout << "ff_int" << endl; }
void ff(unsigned char){ cout << "ff_unsigned_char" << endl; }
void demo_enum()
{
//前置声明
enum intValues :unsigned long long;//不限定作用域,必须制定成员类型
enum class peppers;//限定作用域的可以使用默认成员类型Int
enum color{ red, yellow, green};//没有class或struct,为全局的
//enum stoplight{red,yellow,green}; 重定义枚举成员
enum class peppers{red=5,yellow,green=9};
color eyes = green;
//peppers p = green; //error:peppers的枚举成员不在有效的作用域中,
//color::green在,但类型错误
color har = color::red;
peppers p2 = peppers::red;
//默认情况下.枚举类型从0开始,之后成员为之前的枚举成员的值加1
int x = yellow;//x = 1
cout << x << endl;
int y = (int)peppers::yellow;//限定作用域的枚举类型不会进行隐式转换
cout << y << endl;
enum intValues :unsigned long long{
charTyp = 255, shortTyp = 65535, intTyp = 65535,
longTyp = 4294967295UL, long_longTyp = 18446744073709551615ULL,
};
Tokens curTok = INLINE;
ff(128);
ff(INLINE);
ff(curTok);
unsigned char uc = VIRTUAL;
ff(VIRTUAL);
ff(uc);
}
类成员指针(19.4)
- 初始化时,我们令其指向类的某个成员,但不指定该成员所属的对象,直到使用成员指针时,才提供成员所属对象
- 使用数据成员指针
//pdata可以指向一个常量(非常量)Screen对象的string成员
const string Screen::*pdata;
//利用返回数据成员指针的函数,用于获取私有类
pdata = Screen::data();
Screen myScreen, *pScreen = &myScreen;
//使用数据成员指针
auto s = myScreen.*pdata;//' '
auto s2 = pScreen->*pdata;//' '
cout << s << endl;
cout << s2 << endl;
- 成员函数与相应指针之间不像普通函数可以自动转换
- 使用成员函数指针
//调用运算符前括号不可少,由于调用运算符优先级高于指针向成员运算符的优先级
char c1 = (pScreen->*pmf)();
char c2 = (myScreen.*pmf2)(0, 0);
//使用别名
using Action =
char(Screen::*)(Screen::pos, Screen::pos)const;
Action get = &Screen::get;
Screen& action(Screen&, Action = &Screen::get);
//成员函数指针表
myScreen.move(Screen::HOME);
myScreen.move(Screen::DOWN);
- 成员指针并不是可调用对象,利用function,mem_fn或bind产生可调用对象,实际上还是自动地在对象上调用函数指针
vector<string*>pvec;
//function< bool (const string*) > fp = &string::empty;
//fp 接受一个指向string的指针, 然后使用->*调用empty
//相当于if(((*it)->*p())it为一内部迭代器
//find_if(pvec.begin(), pvec.end(), fp);
//利用mem_fn可以从成员指针类型推断可调用对象类型
auto f = mem_fn(&string::empty);
find_if(pvec.begin(), pvec.end(), mem_fn(&string::empty));
f(*pvec.begin());
f(pvec[0]);
//使用bind生产可调用对象
auto it = find_if(pvec.begin(), pvec.end(),
bind(&string::empty, _1));
嵌套类(19.5)
- 嵌套类的名字在外层作用域中是可见的.二者相互之间没有特殊的访问权限
- 嵌套类的静态成员定义将位于外层作用域之外
- 外层类的对象与嵌套类的对象没有任何关系
union类型(19.6)
- 不能含有引用类型的成员
- 没有继承特性(不可做派生类,基类,无虚函数)
- 由于为
union
的一个数据成员赋值会令其他数据成员变成未定义的状态,不可访问 - 匿名的
union
视为一个当前作用域内可直接访问的对象.里面的对象可直接使用,可以认为是许多已定义了的对象共享一块内存.同时,不能包含受保护的成员或私有成员. - 类中的union含有类类型成员时,必须自己显示进行释放,拷贝,赋值操作.
局部类(19.7)
- 局部类所有成员,包括函数都必须完整定义在类的内部,不允许声明静态数据成员.
- 局部类只能访问外层作用域定义的类型名,静态变量与枚举成员.普通的局部变量不能被该局部类使用.
- 局部类内的嵌套类也是局部类
固有的不可移植的特性(19.8)
- 类可将(非静态)(一般为无符号数)数据成员定义成位域,一个位域中含有一定数量的二进制位.
- 取地址符
&
不能用于位域
volatile
表明它可能在程序控制或检测之外被改变.编译器不应该对这样的对象进行优化. 因而它其实与const
也不冲突voltile
限定符行为很像const
.比如
volatile int v; volatile int*ivp = &v;
//error int *ip = &v; //必须使用指向volatile的指针
但有一个重要区别:
我们不能使用合成的拷贝/移动构造函数与赋值运算符初始化volatile
对象或从volatile
对象赋值.- 只有
voltile
函数才能被volatile
对象调用
- 链接指示来指出任意非C++函数所用的语言.
- 编译器检查其调用方式与处理普通的C++函数的方式相同
- 要求我们必须有权访问语言的编译器,并且这个编译器与当前C++编译器是兼容的
- 链接指示不能出现在类定义或函数定义的内部,同样的链接指示必须出现在函数的每个声明中.
extern "C" size_t strlen(const char *)
extern "C" {
int strcmp(const char*,const char*);
char *strcat(char*,const char*);
}
extern "C"{
#include<string.h>
}
- 链接指示对整个声明有效,也就是对函数而言,返回类型,形参等都是C的.它也不能使用C++中的类与对象.
- C支持重载,因而一组重载中只会有一个是C的.凡是C不支持的那么都是不能使用的.