您的位置:首页 > 编程语言 > C语言/C++

CppPrimer笔记 Chapter19 特殊工具与技术

2017-03-14 23:22 363 查看

CppPrimer笔记 Chapter19 特殊工具与技术

标签: Cpp

CppPrimer笔记 Chapter19 特殊工具与技术
控制内存分配191

运行时类型识别RTTI192

枚举类型193

类成员指针194

嵌套类195

union类型196

局部类197

固有的不可移植的特性198

控制内存分配(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不支持的那么都是不能使用的.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: