访问空指针程序崩溃的底层剖析
转化运算符
const_cast转换符是用来移除变量的const或volatile限定符。 const_cast转换运算符用来去除变量的底层const属性,这里的变量的类型是指针或者引用类型,因为普通类型并没有底层const属性这个说法。 c++ const_cast转换运算符与volatile关键字
断言
运行时断言:assert 静态断言:C++11静态断言 static_assert(断言表达式必须是常量表达式) static_assert是编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失。
赋值运算符、拷贝构造、右值
const T&与T const&等
友元
被声明为友元的函数或者类B,则可以访问当前类A的私有接口和变量(通俗解释就是,B是A的好朋友,那么B就可以去访问好朋友A的私有数据,或者A就愿意公开自己的私有数据给好朋友B)。 友元函数:在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元(把friend 标志置于函数声明的最前),这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。
class CCar
{
private:
int price;
// 声明友元,该函数内部可以使用cars[i]对象直接访问price私有成员变量
friend int MostExpensiveCar(CCar cars[], int total);
friend void CDriver::ModifyCar(CCar* pCar); // 声明友元
};
友元类:一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:friend class 类名;
class CCar
{
private:
int price;
friend class CDriver; //声明 CDriver 为友元类,那么CCar的私有变量price就让CDriver类访问到
};
class CDriver
{
public:
CCar myCar;
void ModifyCar() //改装汽车
{
myCar.price += 1000; //因CDriver是CCar的友元类,故此处可以访问其私有成员
}
};
如何访问类的私有成员
Resource Acquisition Is Initialisation (RAII)
RAII是一种使用在面向对象语言中的资源(内存,互斥锁,或者文件描述符)管理机制 意味着任何资源的获取都应该发生在类的构造函数中,资源的释放应发生在析构函数中。
头文件少用前置声明
头文件的前置声明:
class InspectorEventModel;
class EventTermModel;
class EventNodeModel;
class EventModelBase;
class ResultTermModel;
class ConditionTermModel;
class InspectorEvent : public InspectorBase
容易出现编译问题。 在如下的调用中,总提示m_inspectorEvent->GetInspectorEventModel() 处有对QVariant的错误。主要是调用setContextProperty接口时,调用到了void setContextProperty(const QString &, const QVariant &); 这个,而不是void setContextProperty(const QString &, QObject *); ,而QVariant 是不允许传递VOID * 指针进去的。因为inline QVariant(void *) = delete; QVariant把对于传入VOID * 指针的构造声明为了delete 。 而此时,另外一个相同的代码,确可以成功编译,然后点进去setContextProperty 对于它的调用,确是走入了void setContextProperty(const QString &, QObject *); ,说明成功编译的代码,此处GetInspectorEventModel 返回的值被识别为了一个正常的QObject指针类型(或者说是其相关的子类类型),而编译失败的返回的值被识别为了一个VOID * ,这里可以大致猜想到,CPP文件没法识别返回值,而转为了VOID * 。此时就往GetInspectorEventModel 返回的值是否添加了头文件识别上去查。 果然,因为GetInspectorEventModel 的头文件中对于该返回值做了前置声明,而它的实现的CPP文件内,没有对这个返回值添加头文件引用,导致无法识别这个类型!!!!
engine()->rootContext()->setContextProperty("inspectorEventModel",
m_inspectorEvent->GetInspectorEventModel());
// m_inspectorEvent->GetInspectorEventModel()的定义
InspectorEventModel *InspectorEvent::GetInspectorEventModel() const
{
return static_cast<InspectorEventModel *>(m_inspectorModel.get());
}
使用typedef定义别名
在类中定义,让这个别名归属于此类的范畴中。
class AtomCommand
{
public:
typedef std::shared_ptr<AtomCommand> ptr;
typedef QVector<ptr> ptrVec;
友元
new 和 make_shared的区别
内存上:先new然后赋值的方式,会导致内存碎片化 make_shared的方法分配内存,不会导致内存产生过多的碎片
A* a = new A; //①
std::shared_ptr<A> pa(a);
std::shared_ptr<A> pa1 = std::make_shared<A>(1); //②
通过①的方式,是先在堆上分配一块内存,然后在堆上再建一个智能指针控制块,这两个东西是不连续的,会造成内存碎片化 通过②的方式,是直接在堆上新建一块足够大的内存,其中包含两部分,上面是内存(用来使用),下面是控制块(包含引用计数),然后用A的构造函数去初始化分配的内存(分配一块内存的步骤:先分配内存,再进分配的内存调用构造函数进行构造,构造完毕才能使用) Std::shared_ptr管理两个实体:
智能指针
virtual函数中默认参数的表现
结论:小心使用。使用时也要保持基类和子类的默认参数一致!不一致的情况下会有可能引发问题。 即虚函数,执行那个函数是运行时决定的,但是其默认参数却是静态决定的,用了什么类型的指针就使用哪套默认参数。 参考:Effective C++ .37 virtual函数中默认参数的表现 子类成员函数需要定义一个默认参数,那么基类的成员函数一定需要定义一个默认的参数。 基类定义的成员函数,需要附带默认参数。
virtual inline void SetDisplayName(const std::string &name = "")
{
m_displayName = name;
}
定义ModelPrivate
|