本文主要是介绍Skia源码点滴,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
今天在看SkCanvas类的save()方法时看到两行code,有点不解,具体如下:
int SkCanvas::save() {this->willSave(kMatrixClip_SaveFlag);return this->internalSave(kMatrixClip_SaveFlag);
}
int SkCanvas::internalSave(SaveFlags flags) {int saveCount = this->getSaveCount(); // record this before the actual save<strong> MCRec* newTop = (MCRec*)fMCStack.push_back();new (newTop) MCRec(fMCRec, flags); // balanced in restore()</strong></strong>fMCRec = newTop;if (SkCanvas::kClip_SaveFlag & flags) {fClipStack.save();}return saveCount;
}
加粗的两行code中,第二行的new关键字用法有些不解,然后百度之,解释如下:
关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。
当然,如果我们创建的是简单类型的变量,那么第二步会被省略。
假设有如下类A:
class A {
int i;
public: A(int _i) :i(_i*_i) {}
void Say() { printf("i=%dn", i);
} ;
//调用new:
A* pa = new A(3);
那么上述动态创建一个对象的过程大致相当于以下三句话(只是大致上):
A* pa = (A*)malloc(sizeof(A));
pa->A::A(3);
return pa;
C++中一提到new,至少可能代表以下三种含义:new operator、operator new、placement new.
new operator就是我们平时所使用的new,其行为就是前面所说的三个步骤,我们不能更改它。operator new是用来重载new操作符,示例如下:
class A {
public: void* operator new(size_t size) {printf("operator new calledn"); return ::operator new(size); //这里通过::operator new调用了原有的全局的new}
};
new的第三种形态——placement new是用来实现定位构造的,因此可以实现new operator三步操作中的第二步,也就是在
取得了一块可以容纳指定类型对象的内存后,在这块内存上构造一个对象。
#include <new.h>
void main() {char s[sizeof(A)];A* p = (A*)s;new(p) A(3); //p->A::A(3);p->Say();
}
对头文件<new>或<new.h>的引用是必须的,这样才可以使用placement new。这里
new(p) A(3);
这种奇怪的写法便是placement new了,它实现了在指定内存地址上用指定类型的构造函数来构造一个对象的功能,后面A(3)就是对构造函数的显式调用。这里不难发现,这块指定的地址既可以是栈,又可以是堆,placement对此不加区分。但是,除非特别必要,不要直接使用placement new ,这毕竟不是用来构造对象的正式写法,只不过是new operator的一个步骤而已。使用new operator地编译器会自动生成对placement new的调用的代码,因此也会相应的生成使用delete时调用析构函数的代码。如果是像上面那样在栈上使用了placement new,则必须手工调用析构函数,这也是显式调用析构函数的唯一情况: p->~A();
百度完毕。
<strong> MCRec* newTop = (MCRec*)fMCStack.push_back();new (newTop) MCRec(fMCRec, flags); // balanced in restore()</strong>
SkCanvas类的save()方法在new一个新的栈帧时提前在堆中分配好了内存,然后在这个分配好的内存中构造了栈帧。
这篇关于Skia源码点滴的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!