C++语言学习(十四)——C++类成员函数调用分析

C++语言学习(十四)——C++类成员函数调用分析

一、C++成员函数

1、C++成员函数的编译

C++中的函数在编译时会根据命名空间、类、参数签名等信息进行重新命名,形成新的函数名。函数重命名的过程通过一个特殊的Name Mangling(名字编码)算法来实现。Name Mangling算法是一种可逆的算法,既可以通过现有函数名计算出新函数名,也可以通过新函数名逆向推导出原有函数名。
Name Mangling算法可以确保新函数名的唯一性,只要命名空间、所属的类、参数签名等有一个不同,那么产生的新函数名也不同。
不同的编译器有不同的?Name Mangling 算法,产生的函数名也不一样。

2、this指针

this指针属性如下:
A、名称属性:标识符this表示。
B、类型属性:classname const
C、值属性:表示当前调用该函数对象的首地址。
D、作用域:this指针是编译器默认传给类中非静态函数的隐含形参,其作用域在非静态成员函数的函数体内。
E、链接属性:在类作用域中,不同类的非静态成员函数中,this指针变量的链接属性是内部的,但其所指对象是外部的,即this变量是不同的实体,但指向对象是同一个。
F、存储类型:this指针是由编译器生成,当类的非静态成员函数的参数个数一定时,this指针存储在ECX寄存器中;若该函数参数个数未定(可变参数函数),则存放在栈中。
this指针并不是对象的一部分,this指针所占的内存大小是不会反映在sizeof操作符上的。this指针的类型取决于使用this指针的成员函数类型以及对象类型。
类的成员函数默认第一个参数为T
const register this。
this在成员函数的开始执行前构造,在成员函数执行结束后清除。

二、C++成员函数指针

1、C++成员函数指针简介

C++语言规定,成员函数指针具有contravariance特性,即基类的成员函数指针可以赋值给派生类的成员函数指针,C++语言提供了默认的转换方式,但反过来不行。
C++编译器在代码编译阶段会对类对象调用的成员函数进行静态绑定(虚函数进行动态绑定),类成员函数的地址在代码编译时就确定,类成员函数地址可以使用成员函数指针进行保存。
成员函数指针定义语法如下:

ReturnType (ClassName::* pointerName) (ArgumentLList);
ReturnType:成员函数返回类型
ClassName: 成员函数所属类的名称
Argument_List: 成员函数参数列表
pointerName:指针名称
class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

成员函数指针语法极其严格:
A、不能使用括号:例如&(Test::print)不对。
B、?必须有限定符:例如&print不对,即使在类ClassName作用域内也不行。
C、必须使用取地址符号:直接写Test::print不行,必须写:&Test::print。
Test类的成员函数print的函数指针声明如下:
void (Test::*pFun)();
初始化如下:
pFunc = &Test::print;
Test类的成员函数print的函数指针声明及初始化如下:
void (Test::* pFunc)() = &Test::print;
通常,为了简化代码,使用typedef关键字。

typedef void (Test::*pFunc)();
pFunc p = &Test::print;

可以通过函数指针调用成员函数,示例代码如下:

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

int main(int argc, char *argv[])
{
    void (Test::* pFunc)() = &Test::print;
    Test test;
    //通过对象调用成员函数
    (test.*pFunc)();//Test::print
    Test* pTest = &test;
    //通过指针调用成员函数
    (pTest->*pFunc)();//Test::print
    //pFunc();//error
    //error: must use ‘.*‘ or ‘->*‘ to call pointer-to-member
    //function in ‘pFunc (...)‘, e.g. ‘(... ->* pFunc) (...)‘

    return 0;
}

上述代码中,.*pFunc将pFunc绑定到对象test,-&gt;*pFunc绑定pFunc到pTest指针所指向的对象。
成员函数指针不是常规指针(保存的是某个确切地址),成员函数指针保存的是成员函数在类布局中的相对地址。

2、C++成员函数地址

C++成员函数使用thiscall函数调用约定。C++静态成员函数、普通成员函数的函数地址在代码区,虚成员函数地址是一个相对地址。

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
        cout << "Parent(int i, int j): " << this << endl;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual void sayHello()
    {
        cout << "Parent::sayHello()" << endl;
    }
    virtual void func()
    {
        cout << "Parent::func()" << endl;
    }
    virtual ~Parent()
    {
        cout << "~Parent(): " << this << endl;
    }
    static void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int v)
    {
        return m_i + m_j + v;
    }
protected:
    int m_i;
    int m_j;
};

int main(int argc, char *argv[])
{
    cout <<&Parent::display<<endl;
    cout <<&Parent::print<<endl;
    cout <<&Parent::sayHello<<endl;
    cout <<&Parent::func<<endl;

    return 0;
}

上述代码中,打印出的所有的成员函数的地址为1。原因在于输出操作符<<没有对C++成员函数指针类型进行重载,C++编译器将C++成员函数指针类型转换为bool类型进行了输出,所以所有的输出为1。因此,C++成员函数地址进行打印时不能使用cout,可以用printf输出,因为printf可以接收任意类型的参数,包括__thiscall类型。

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
        cout << "Parent(int i, int j): " << this << endl;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual void sayHello()
    {
        cout << "Parent::sayHello()" << endl;
    }
    virtual void func()
    {
        cout << "Parent::func()" << endl;
    }
    virtual ~Parent()
    {
        cout << "~Parent(): " << this << endl;
    }
    static void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int v)
    {
        return m_i + m_j + v;
    }
protected:
    int m_i;
    int m_j;
};

int main(int argc, char *argv[])
{
    //静态成员函数
    cout << "static member function addree:" << endl;
    printf("0x%p\n", &Parent::display);
    printf("0x%p\n", Parent::display);
    //普通成员函数
    cout << "normal member function addree:" << endl;
    printf("0x%p\n", &Parent::add);
    cout << "virtual member function addree:" << endl;
    //虚成员函数
    printf("%d\n", &Parent::print);//1
    printf("%d\n", &Parent::sayHello);//5
    printf("%d\n", &Parent::func);//9

    return 0;
}

3、C++编译器成员函数指针的实现

C++编译器要实现成员函数指针,必须解决下列问题:
A、成员函数是不是虚函数。
B、成员?函数运行时,需不需要调整this指针,如何调整。
不需要调整this指针的情况如下:
A、继承树最顶层的类。
B、单继承,若所有类都不含有虚函数,那么继承树上所有类都不需要调整this指针。
C、单继承,若最顶层的类含有虚函数,那么继承树上所有类都不需要调整this指针。
可能需要进行this指针调整的情况如下:
A、多继承
B、单继承,最顶的base class不含virtual function,但继承类含虚函数,继承类可能需要进行this指针调整。
Microsoft VC对C++成员函数指针的实现采用的是Microsoft一贯使用的Thunk技术。Microsoft将成员函数指针分为两种:

struct pmf_type1{
    void* vcall_addr;
};

struct pmf_type2{
    void* vcall_addr;
    int  delta;  //调整this指针用
};

vcall_addr是Microsoft?的Thunk技术核心所在。vcall_addr是一个指针,隐藏了它所指的函数是虚拟函数还是普通函数的区别。如果所指的成员函数是一个普通成员函数,vcall_addr是成员函数的函数地址。如果所指的成员函数是虚成员函数,那么vcall_addr指向一小段代码,这段代码会根据this指针和虚函数索引值寻找出真正的函数地址,然后跳转到真实的函数地址处执行。
Microsoft根据情况选用函数指针结构表示成员函数指针,使用Thunk技术(vcall_addr)实现虚拟函数/非虚拟函数的自适应,在必要的时候进行this指针调整(使用delta)。
GCC对于成员函数指针统一使用下面的结构进行表示:

struct
{
    void* __pfn;  //函数地址,或者是虚拟函数的index
    long __delta; // offset, 用来进行this指针调整
};

不管是普通成员函数,还是虚成员函数,信息都记录在__pfn。一般来说因为对齐的关系,函数地址都至少是4字节对齐的。即函数地址的最低位两个bit总是0。?GCC充分利用了这两个bit。如果是普通的函数,__pfn记录函数的真实地址,最低位两个bit就是全0,如果是虚成员函数,最后两个bit不是0,剩下的30bit就是虚成员函数在函数表中的索引值。
GCC先取出函数地址最低位两个bit看看是不是0,若是0就使用地址直接进行函数调用。若不是0,就取出前面30位包含的虚函数索引,通过计算得到真正的函数地址,再进行函数调用。
GCC和Microsoft对成员函数指针实现最大的不同就是GCC总是动态计算出函数地址,而且每次调用都要判断是否为虚函数,开销自然要比Microsoft的实现要大一些。
在this指针调整方面,GCC和Mircrosoft的做法是一样的。不过GCC在任何情况下都会带上__delta变量,如果不需要调整,__delta=0
GCC的实现比Microsoft简单,在所有场合其实现方式都是一样的。

4、C++成员函数指针的限制

C++语言的规定,基类的成员函数指针可以赋值给派生类的成员函数指针,不允许继承类的成员函数指针赋值给基类成员函数指针。
?C++规定编译器必须提供一个从基类成员函数指针到继承类成员函数指针的默认转换。C++编译器提供的默认转换最关键的就是this指针调整。
因此,一般情况下不要将继承类的成员函数指针赋值给基类成员函数指针。不同C++编译器可能有不同的表现。
解决方案:
A、不要使用static_cast将继承类的成员函数指针赋值给基类成员函数指针,如果一定要使用,首先确定没有问题。
B、如果一定要使用static_cast,注意不要使用多继承。
C、如果一定要使用多继承的话,不要把一个基类的成员函数指针赋值给另一个基类的函数指针。
D、单继承要么全部不使用虚函数,要么全部使用虚函数。不要使用非虚基类,却让子类包含虚函数。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    Parent* pTestA = &childA;
    typedef void (Parent::*pPrintFunc)();
    pPrintFunc pPrint = &Parent::print;
    typedef double (Parent::*pSumFunc)();
    pSumFunc pSum = &Parent::sum;
    typedef void (Parent::*pDisplayFunc)();
    pDisplayFunc pDisplay = &Parent::display;

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);
    //不能将派生类的成员函数指针赋值给基类的函数指针
    //pPrint = &ChildA::print;//error
    //可以将基类的成员函数指针赋值给派生类
    void (ChildA::*pChildPrintFunc)() = pPrint;
    (childA.*pChildPrintFunc)();//Parent::print
    void (*p)() = reinterpret_cast<void (*)()>(pPrint);
    p();

    return 0;
}

5、静态成员函数指针

对于静态成员函数,函数体内部没有this指针,与类的其它成员函数共享类的命名空间,但静态成员函数并不是类的一部分,静态成员函数与常规的全局函数一样,成员函数指针的语法针对静态成员函数并不成立。
静态成员函数的函数指针定义语法如下:

ReturnType (* pointerName) (ArgumentLList);
ReturnType:成员函数返回类型
Argument_List: 成员函数参数列表
pointerName:指针名称

静态成员函数的函数指针的使用与全局函数相同,但静态成员函数指针保存的仍旧是个相对地址。

#include <iostream>

using namespace std;

class Test
{
public:
    static void print()
    {
        cout << "Test::print" << endl;
    }
};

int main(int argc, char *argv[])
{
    void (* pFunc)() = &Test::print;
    cout << pFunc << endl;//1
    //直接调用
    pFunc();//Test::print
    (*pFunc)();//Test::print
    Test test;
    //(test.*pFunc)();//error
    Test* pTest = &test;
    //(pTest->*pFunc)();//error

    return 0;
}

6、普通成员函数指针

非静态、非虚的普通成员函数指针不能直接调用,必须绑定一个类对象。
普通函数指针的值指向代码区中的函数地址。如果强制转换为普通函数指针后调用,成员函数内部this指针访问的成员变量将是垃圾值。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    Parent* pTestA = &childA;
    typedef void (Parent::*pPrintFunc)();
    pPrintFunc pPrint = &Parent::print;
    typedef double (Parent::*pSumFunc)();
    pSumFunc pSum = &Parent::sum;
    typedef void (Parent::*pDisplayFunc)();
    pDisplayFunc pDisplay = &Parent::display;

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);
    //绑定类对象进行调用
    (pTestA->*pPrint)();
    (pTestA->*pSum)();
    (pTestA->*pDisplay)();
    //强制转换为普通函数指针
    void (*p)() = reinterpret_cast<void (*)()>(pPrint);
    p();//打印随机值

    return 0;
}

7、虚成员函数指针

C++通过虚函数提供了运行时多态特性,编译器通常使用虚函数表实现虚函数。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    virtual void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

class ChildB : public Parent
{
public:
    ChildB(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildB::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildB::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildB::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    //childA.display();//error,编译时private不可访问
    ChildB childB(100,200,3.14);
    Parent* pTestA = &childA;
    Parent* pTestB = &childB;
    typedef void (Parent::*pVPrintFunc)();
    pVPrintFunc pPrint = &Parent::print;

    (parent.*pPrint)();//Parent::print
    (pTestA->*pPrint)();//ChildA::print,多态
    (pTestB->*pPrint)();//ChildB::print,多态

    typedef double (Parent::*pVSumFunc)();
    pVSumFunc pSum = &Parent::sum;

    (parent.*pSum)();//Parent::sum
    (pTestA->*pSum)();//ChildA::sum,多态
    (pTestB->*pSum)();//ChildB::sum,多态

    typedef void (Parent::*pVDisplayFunc)();
    pVDisplayFunc pDisplay = &Parent::display;

    (parent.*pDisplay)();//Parent::display
    (pTestA->*pDisplay)();//ChildA::display,多态
    (pTestB->*pDisplay)();//ChildB::display,多态

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);

    return 0;
}

虚成员函数指针的值是一个相对地址,表示虚函数在虚函数表中,离表头的偏移量+1。
当一个对象调用虚函数时,首先通过获取指向虚函数表指针的值得到虚函数表的地址,然后将虚函数表的地址加上虚函数离表头的偏移量即为虚函数的地址。?

8、成员函数指针示例

成员函数指针的一个重要应用是根据输入来生成响应事件,使用不同的处理函数来处理不同的输入。

#include <stdio.h>
#include <iostream>
#include <string.h>

using namespace std;

//虚拟打印机
class Printer {
public:
    //复制文件
    void Copy(char *buff, const char *source)
    {
        strcpy(buff, source);
    }
    //追加文件
    void Append(char *buff, const char *source)
    {
        strcat(buff, source);
    }
};

//菜单中两个可供选择的命令
enum OPTIONS { COPY, APPEND };

//成员函数指针
typedef void(Printer::*PTR) (char*, const char*);

void working(OPTIONS option, Printer *machine,
             char *buff, const char *infostr)
{
    // 指针数组
    PTR pmf[2] = { &Printer::Copy, &Printer::Append };
    switch (option)
    {
    case COPY:
        (machine->*pmf[COPY])(buff, infostr);
        break;
    case APPEND:
        (machine->*pmf[APPEND])(buff, infostr);
        break;
    }
}

int main() {
    OPTIONS option;
    Printer machine;
    char buff[40];

    working(COPY, &machine, buff, "Strings ");
    working(APPEND, &machine, buff, "are concatenated!");

    std::cout << buff << std::endl;
}

// Output:
// Strings are concatenated!

三、C++类成员函数的调用分析

1、成员函数调用简介

类中的成员函数存在于代码段。调用成员函数时,类对象的地址作为参数隐式传递给成员函数,成员函数通过对象地址隐式访问成员变量,C++语法隐藏了对象地址的传递过程。由于类成员函数内部有一个this指针,类成员函数的this指针会被调用的类对象地址赋值。因此,如果类成员函数中没有使用this指针访问成员,则类指针为NULL时仍然可以成功对该成员函数进行调用。static成员函数作为一种特殊的成员函数,函数内部不存在this指针,因此类指针为NULL时同样可以成功对静态成员函数进行调用。

#include <iostream>
#include <string>

using namespace std;

namespace Core {

class Test
{
public:
    Test(int i)
    {
        this->i = i;
    }
    void print()
    {
        cout << "i = " << i << endl;
    }
    void sayHello()
    {
        cout << "Hello,Test." << endl;
    }
    static void printHello()
    {
        cout << "Hello,Test." << endl;
    }
private:
    int i;
};

}

int main()
{
    using namespace Core;
    Core::Test* ptest = NULL;
    ptest->sayHello();//Hello,Test.
    ptest->printHello();
    //定义函数指针类型
    typedef void (Test::*pFunc)();
    //获取类的成员函数地址
    pFunc p = &Test::print;
    Test test(100);
    //调用成员函数
    (test.*p)();//i = 100

    return 0;
}

2、普通成员函数调用机制分析

普通成员函数通过函数地址直接调用。

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

int main()
{
    Test test;
    Test* p = &test;
    p->print();
}

对于非虚、非静态成员函数的调用,如p->print(),C++编译器会生成如下代码:

Test* const this = p;
void Test::print(Test* const this)
{
    cout << "Test::print" << endl;
}

不管指针p是任何值,包括NULL,函数Test::print()都可以被调用,p被作为this指针并当作参数传递给print函数。因此,当传入print函数体内的p指针为NULL时,只要不对p指针进行解引用,函数就能正常调用而不发生异常退出。

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
        sayHello();
    }
    void sayHello()
    {
        cout << "Test::sayHello" << endl;
    }
};

int main()
{
    Test* p = NULL;
    p->print();
}

// output:
// Test::print
// Test::sayHello

3、静态成员函数调用机制分析

静态成员函数通过函数地址进行调用,其调用方式同全局函数。

4、虚成员函数调用机制分析

虚成员函数的调用涉及运行时多态。
当一个对象调用虚函数时,首先通过运行时对象获取指向虚函数表指针的值得到虚函数表的地址,然后将虚函数表的地址加上虚函数离表头的偏移量即为虚函数的地址。?基类对象内部的虚函数表指针指向基类的虚函数表,派生类对象的虚函数表指针指向派生类的虚函数表,确保运行时对象调用正确的虚函数。

原文地址:http://blog.51cto.com/9291927/2148695

时间: 2024-11-05 18:42:33

C++语言学习(十四)——C++类成员函数调用分析的相关文章

C++语言学习(四)——类与对象

C++语言学习(四)--类与对象 一.构造函数(constructor) 1.构造函数简介 C++语言中,构造函数是与类名相同的特殊成员函数.在类对象创建时,自动调用构造函数,完成类对象的初始化.类对象本身是变量,在栈.堆上创建的对象,对象的成员初始化为随机值:在静态存储区创建的对象,对象的成员初始化为0. 2.构造函数的定义 构造函数声明的语法如下:classname(parameters);没有参数的构造函数称为无参构造函数.当类中没有定义构造函数(包括拷贝构造函数)时,编译器默认提供一个无

C语言第十四回合:结构体大集合

C语言第十四回合:结构体大集合 [学习目标] 1.        结构体 2.        结构体数组 3.        结构体指针 结构体:是数据结构类型,把有内在联系的不同类型的数据统一成一个整体,使它们相互关联.是变量的集合,可以单独使用其的成员. A:结构体的定义 使用关键字:struct struct 结构体名 { 类型标识符  成员名1; 类型标识符  成员名2; -- };     //分号一定不能省 PS: (1)结构定义并不预留内存,结构体变量的定义才引起存储分配 (2) 

Oracle学习(十四):管理用户安全

--用户(user) SQL> --创建名叫 grace 密码是password 的用户,新用户没有任何权限 SQL> create user grace identified by password; 验证用户: 密码验证方式(用户名/密码) 外部验证方式(主机认证,即通过登陆的用户名) 全局验证方式(其他方式:生物认证方式.token方式) 优先级顺序:外部验证>密码验证 --权限(privilege) 用户权限有两种: System:允许用户执行对于数据库的特定行为,例如:创建表.

Go语言学习笔记(四) [array、slices、map]

日期:2014年7月22日 一.array[数组] 1.定义:array 由 [n]<type> 定义,n 标示 array 的长度,而 <type> 标示希望存储的内容的类型. 例如: var arr[10] int arr[0] = 1 arr[1] = 2 数组值类型的:将一个数组赋值给 另一个数组,会复制所有的元素.另外,当向函数内传递一个数组的时候,它将获得一个数组的副本,而不是数组的指针. 2.数组的复合声明.a :=[3]int{1,2,3}或简写为a:=[...]i

Python 学习笔记 - 面向对象(类成员)

上一篇学习了Python面向对象的3大特性,封装,继承和多态,这一篇继续学习类成员,包括字段,方法,属性以及他们的修饰符. 1.字段 字段分为静态字段和普通字段.静态字段属于类,而普通字段属于对象,因此静态字段在内存中只保存一份,而普通字段在每个对象中都保存了一份.定义的时候静态字段定义在类的范围里面,而普通字段定义在方法里面. 例如: >>> class Foo:     # 字段(静态字段)     CC = 123     def __init__(self):         #

(原创)c#学习笔记10--定义类成员02--类成员的其他议题02--调用重写或隐藏的基类方法

10.2.2  调用重写或隐藏的基类方法 无论是重写成员还是隐藏成员,都可以在派生类的内部访问基类成员.这在许多情况下都是很有用的,例如: 要对派生类的用户隐藏继承的公共成员,但仍能在类中访问其功能. 要给继承的虚拟成员添加实现代码,而不是简单地用重写的新执行代码替换它. 为此,可以使用 base 关键字,它表示包含在派生类中的基类的实现代码(在控制构造函数时,其用法是类似的,如第9所述),例如: public class MyBaseClass { public virtual void Do

android NDK 实用学习(四)-类缓存

1,为什么需要类缓存: 答:由于频繁的查找类及类成员变量需要很大的时间与空间开销,可参考如下文章: http://www.ibm.com/developerworks/cn/java/j-jni/ http://www.28im.com/java/a2379737.html 2,缓存时需要在java类使用static,如下: 1 package com.dasea.test.core; 2 public class TestSetData { 3 // 主要是类ID和字段ID,方法ID的缓存 4

C++并发类成员函数调用(练习1)

一般类成员函数开线程格式 std::thread t1(&类名::函数,&实例化对象,参数....) ||std::thread t1(std::bind(&&类名::函数,&实例化对象,参数....)) 1 #include <iostream> 2 #include <mutex> 3 #include <thread> 4 #include <vector> 5 #include <string> 6

C语言--&gt;(十四)结构体、宏、编译

知识点: 1.结构体 struct 2.typedef关键字 3.宏的定义 4.宏与函数的区别 5.文件包含和多文件编译 6.条件编译 ===========================结构体 思考:如果现在希望保存一个学生的信息,姓名,身高,年龄,地址,该如何保存 char name[64]; float height; int age; char addr[64]; 1.什么是结构体 struct 结构体指的是一种数据结构,是c语言中复合数据类型的一 种多种不同数据类型的集合 2.结构体