从C到C++看面相对象(深入了解C++的成员函数)

来源:互联网 发布:php header cookie 编辑:程序博客网 时间:2024/06/05 10:22

我们都知道C是一门面相过程的语言,在C的世界里是没有面相对象这个概念的,但是C语言为我们提供的神兵利器,仍旧可以让我们使用面相对象的思维方式
在C语言里,我们每做一个操作,都需要写一个函数,但是该函数都是过程化的,但是我们有两种神兵利器,一个叫指针,一个叫结构体
为什么这么说呢?
面相对象的最基本的功能就是对数据的封装,在C语言的世界里,我们有结构体这个法宝,同样可以将数据打包整整体,然后通过指针的方式,将结构体作为参数在函数中进行传递
举个例子

struct Book {    char name[32]; //书名    char author[32]; //作者名    int total; //总页数    int price;//价格};typedef Book Book;//初始化一本书void book_init(Book *book, const char *name, const char *author, int total, int price){    strcpy(book->name, name);    strcpy(book->author, author);    book->total = total;    book->price = price;}void book_update_price(Book *book, int newPrice){    book->price = price;}

以上的例子很简单,初始化一本书以及更新书价,在使用的时候,我们只需要像以下方式调用

Book book;book_init(&book, "WPF 编程宝典", "Matthew MacDonald", 934, 128);//...book_update_price(&book, 100); //降价

以上的这些使用的都是面相过程的思维。
所谓面向过程编程,就好比“让某某去做某事”,而面向对象呢,就好比“某某去做某事”。从字面意思来看,面向过程貌似多了个“高级领导”,而面向对象显得更自由。

我们再变一下:

struct Book{    char name[32];    char author[32];    int price;    void (*init)(Book *book, const char *name, const char *author, int price);    void (*update_price)(Book *book, int newPrice);}//初始化一本书void book_init(Book *book, const char *name, const char *author, int price){    strcpy(book->name, name);    strcpy(book->author, author);    book->total = total;    book->price = price;}void book_update_price(Book *book, int newPrice){    book->price = price;}//创建一本书void book_create(Book *book){    book->init = book_init;    book->update_price = book_update_price;}

然后你就可以像以下一样使用了

Book book;book_create(&book);book.init(&book, "WPF 编程宝典", "Matthew MacDonald", 128);//...book.update_price(&book, 100);

使用这种方式来调用,看起来有了一种“书做了某某事”的面向对象的错觉

C++正是借鉴了这一点,从而产生了Class(类)

当我们定义一个class的时候,我们自己定义的成员函数就使用了我们上面的思想,参考如下代码

class Book {    char name[32];    char author[32;    int price;    void init(const char *name, const char *author, int price)    {        strcpy(this->name, name);        strcpy(this->author, author);        this->price = price;    }    void updatePrice(int price)    {        this->price = price;    }};

我们是用起来显得更简单

Book book;book.init("WPF 编程宝典", "Matthew MacDonald", 128)://...book.updatePrice(100);

看起来与我们使用C语言模仿的面向对象是不是很像?
接下来我们注意到一个关键字this, 指的是调用函数的某个对象,谁调用了,this指的就是谁,上面的book调用init和updatePrice的时候,this指的就是book这个对象。

我想你应该明白了,C++的面向对象,就是使用了我们的上述模拟面向对象的思维,然后C++自己将对象本身作为一个隐含的参数传递给了我们的成员函数,当然,这些并不包括C++面向对象中更强大的继承和多态。
到此我觉得你应该明白了C++成员函数与普通函数之间的区别以及其内部的原理。
再看下面代码

class Student{public:    Student(const char *name) { strcpy(this->name, name);}    void makeTest() { std::cout<<"我在考试"<<std::endl;}    void appear() { std::cout<<"My name is "<<name<<std::endl;private:    char name[32];};

我们在看如下调用

Student *pStu;pStu->makeTest(); // ①pStu->appear(); // ②

很多人都会想到,pStu是一个未初始化的变量,以上①②两种调用都会导致崩溃。再仔细想想看,真的是这样吗?
比较细心的同学会发现,在某些编译器上运行,我们却可以看到①调用后的打印信息,而运行②以后,程序崩溃掉了,我想细心的你应该知道这是什么原因了。
很简单,我们在①处调用的makeTest()函数,翻译成C语言的形式应该是

void makeTest(Student *this){    //打印“我在考试”}makeTest(pStu);

在代码里面,pStu虽然没有初始化,但是在上面的C语言版的makeTest中,我们并没有使用到this这个参数,所以即使这个参数指针是一个野指针或者空指针,都不会出现崩溃现象,但是对于②处的调用,使用的appear()函数中使用到了this, 也就是说,此时的this是一个未初始化的指针(我们称为野指针,不是空指针),这个指针指向什么地方我们并不知道,可能是没有权限操作的一块内存,也可能正好是空指针,也可能是已经分配的某个内存

我们再做一个小实验

class Test{public:    void test()    {        if(this != nullptr) //C++11中引入的nullptr, 表示空指针,感兴趣的可以自己去多多了解C++11        {            std::cout<<"我不是空的"<<std::endl;        } else {            std::cout<<"我是空的"<<std::endl;        }    }};int main(){    Test t;    Test *p = nullptr; //设置初始值,防止使用未初始化指针       p->test();    p = &t;    p->test();    return 0;}

你能猜出最终的打印信息吗?

0 0
原创粉丝点击