其他主题
const_cast运算符
C++提供 const_cast运算符,可以强制去除变量的const 和 volatile 属性. 如
const char* maximum(const char*first,const char* second);
......
char* maxPtr = const_cast<char*>(maximum(s1,s2));
如果没有const_cast<char*>,编译器将会报错,因为无法将const char赋值给char
关于volatile
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。下面举例说明。在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:
short flag;
void test()
{
do1();
while (flag == 0);
do2();
}
这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:
volatile short flag;
所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。
volatile的本意是“易变的”,由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。
mutable类成员
可以用来说明一个类中的成员总是可以被修改,即使它是在const成员函数中被调用
class test
{
public:
int getValue() const
{
times++; // times可修改
return value;
}
private:
int value;
mutable int times=0;
}
命名空间
运算符关键字
C++ 提供运算符关键字代替相应运算符
运算符 | 运算符关键字 | 描述 |
---|---|---|
逻辑运算符关键字 | ||
&& | and | 逻辑与 |
or | l逻辑或 | |
! | not | 逻辑非 |
!= | 不等运算符关键字 | 不等 |
位运算符关键字 | ||
& | bitand | 位与 |
bitor | 位或 | |
^ | xor | 位异或 |
~ | compl | 按位取反 |
按位赋值运算符 | ||
&= | and_eq | |
or_eq | ||
^= | xor_eq |
指向类成员的指针(.* 和 ->*)
上代码
int main()
{
Test test;
test.value = 8;
arrowStar(&test);
dotStar(&test);
return 0;
}
void arrowStar(Test* testPtr)
{
void (Test::*funcPtr)() = &Test::func;
(testPtr->*funcPtr)(); //调用
((*testPtr).*funcPtr)(); //调用
}
void dotStar(Test* testPtr)
{
int (Test::*vPtr) = &Test::value;
cout << (*testPtr).*vPtr << endl; //调用
}
输出:
In func
In func
8
声明一个指向类成员的指针——类名:: *,如 Test:: *.
(Test::*funcPtr)声明了一个指向类成员指针名 funcPtr, 必须用圆括号括起来。
左括号左侧void为成员函数返回值类型,右括号右侧()为成员函数参数列表,此处为空。
&Test::func,取成员函数地址。
. * 和 -> *为两种调用运算符。
多重继承
多重继承声明
class Drived: public Base1, public Base2, private Base3
{
public:
Drived(double,char,int);
int getData() const;
private:
int real;
}
构造函数的初始化
Drived(double d,char c,int i,int r):Base1(d),Base2(c),Base3(i),real(r)
{
}
从多个基类继承同名的成员函数的二义性问题
调用时加入二元作用域运算符解决二义性问题,如:
Drived drive;
drive.Base1::func();
drive.Base2::func();
分别调用Base1和Base2中名为func的函数。
Base1和Base2类型的父指针都可以指向Drived类型地址
菱形继承的二义性问题
会导致子类中包含两个相同的间接基类成员的副本,存在二义性问题,可使用virtual 基类继承(纯虚)来解决间接继承一个基类的多个副本问题。
//***********菱形继承中的二义性问题***************
//*************使用virtual继承*************
#include <iostream>
using namespace std;
class Base
{
public:
virtual void print() const = 0; //纯虚函数
};
class DrivedOne : virtual public Base //virtual基类继承
{
public:
void print() const
{
cout << "DrivedOne\n";
}
};
class DrivedTwo : virtual public Base //virtual基类继承
{
public:
void print() const
{
cout << "DrivedTwo\n";
}
};
class Multiple: public DrivedOne,public DrivedTwo
{
public:
void print() const
{
DrivedTwo::print(); //为避免二义性,使用二元域分辨符
}
};
int main()
{
Multiple both;
DrivedOne one;
DrivedTwo two;
Base *base[3];
base[0] = &both;
base[1] = &one;
base[2] = &two;
for (int i = 0; i < 3;i++)
base[i]->print(); //多态调用
return 0;
}
输出:
DrivedTwo
DrivedOne
DrivedTwo
如果不使用virtual public Base,base[0] = &both;将发生错误
由于DrivedOne和DrivedTwo都使用virtual继承类 Base 的成员,所以编译器保证只有一个 Base 类子对象继承到类 Multiple中。