常州建设工程信息网站,网络运营是干嘛的,wordpress安装地址,软件项目开发的阶段和任务以下内容源于C语言中文网的学习与整理#xff0c;非原创#xff0c;如有侵权请告知删除。
从博文的分析中可以看出#xff0c;对象的内存中只保留了成员变量#xff0c;除此之外没有任何其他信息#xff0c;程序运行时不知道 stu 的类型为 Student#xff0c;也不知道它…以下内容源于C语言中文网的学习与整理非原创如有侵权请告知删除。
从博文的分析中可以看出对象的内存中只保留了成员变量除此之外没有任何其他信息程序运行时不知道 stu 的类型为 Student也不知道它还有四个成员函数 setname()、setage()、setscore()、show()C 究竟是如何通过对象调用成员函数的呢 一、C函数的编译
C和C语言的编译方式不同。
C语言中的函数在编译时名字不变或者只是简单的加一个下划线_例如func() 编译后为 func() 或 _func()。
C中的函数在编译时会根据它所在的命名空间、它所属的类、以及它的参数列表等信息进行重新命名形成一个新的函数名。这个新的函数名只有编译器知道对用户是不可见的。对函数重命名的过程叫做名字编码Name Mangling是通过一种特殊的算法来实现的。
Name Mangling 的算法是可逆的既可以通过现有函数名计算出新函数名也可以通过新函数名逆向推演出原有函数名。Name Mangling 可以确保新函数名的唯一性只要函数所在的命名空间、所属的类、包含的参数列表等有一个不同最后产生的新函数名也不同。
如果你希望看到经 Name Mangling 产生的新函数名可以只声明而不定义函数这样调用函数时就会产生链接错误从报错信息中就可以看到新函数名。请看下面的代码
#include iostream
using namespace std;void display();
void display(int);namespace ns{void display();
}class Demo{
public:void display();
};int main(){display();display(1);ns::display();Demo obj;obj.display();return 0;
}
该例中声明了四个同名函数包括两个具有重载关系的全局函数一个位于命名空间 ns 下的函数以及一个属于类 Demo 的函数。它们都是只声明而未定义的函数。
在 VS2015 下编译源代码可以看到类似下面的错误信息 小括号中就是经 Name Mangling 产生的新函数名它们都以?开始以区别C语言中的_。不同的编译器有不同的 Name Mangling 算法产生的函数名也不一样。__thiscall、cdecl 是函数调用惯例见《函数调用惯例》一文。除了函数某些变量也会经 Name Mangling 算法产生新名字这里不再赘述。 二、成员函数的调用
从上图看出成员函数最终被编译成与对象无关的全局函数如果函数体中没有成员变量那问题就很简单不用对函数做任何处理直接调用即可。但如果成员函数中使用到了成员变量该怎么办呢成员变量的作用域不是全局的如果不经任何处理就无法在函数内部访问。
C规定编译成员函数时要额外添加一个参数把当前对象的指针传递进去通过指针来访问成员变量。这一切都是隐式完成的对程序员来说完全透明就好像这个额外的参数不存在一样。
假设 Demo 类有两个 int 型的成员变量 a 和 b并且在成员函数 display() 中使用到了如下所示
void Demo::display(){coutaendl;coutbendl;
}
那么编译后的代码类似于
void new_function_name(Demo * const p){//通过指针p来访问a、bcoutp-aendl;coutp-bendl;
}
使用obj.display()调用函数时也会被编译成类似下面的形式
new_function_name(obj);
这样通过传递对象指针就完成了成员函数和成员变量的关联。这与我们从表明上看到的刚好相反通过对象调用成员函数时不是通过对象找函数而是通过函数找对象。
最后需要提醒的是Demo * const p中的 const 表示指针不能被修改p 只能指向当前对象不能指向其他对象。通过下一节的讲解我们可以知道这里的p其实就是this指针。 三、this指针详解
this 是 C 中的一个关键字也是一个 const 指针它指向当前对象通过它可以访问当前对象的所有成员。
所谓当前对象是指正在使用的对象。例如对于stu.show();stu 就是当前对象this 就指向 stu。
下面是使用 this 的一个完整示例
#include iostream
using namespace std;class Student{
public:void setname(char *name);void setage(int age);void setscore(float score);void show();
private:char *name;int age;float score;
};void Student::setname(char *name){this-name name;
}
void Student::setage(int age){this-age age;
}
void Student::setscore(float score){this-score score;
}
void Student::show(){coutthis-name的年龄是this-age成绩是this-scoreendl;
}int main(){Student *pstu new Student;pstu - setname(李华);pstu - setage(16);pstu - setscore(96.5);pstu - show();return 0;
}
本例中成员函数的参数和成员变量重名只能通过 this 区分。以成员函数setname(char *name)为例它的形参是name和成员变量name重名如果写作name name;这样的语句就是给形参name赋值而不是给成员变量name赋值。而写作this - name name;后左边的name就是成员变量右边的name就是形参一目了然。
注意this 只能用在类的内部通过 this 可以访问类的所有成员包括 private、protected、public 属性的。
给 this 指针赋值是由编译器自动完成的不需要用户干预用户也不能显式地给 this 赋值。
本例中this 的值和 pstu 的值是相同的。 我们不妨来证明一下给 Student 类添加一个成员函数printThis()专门用来输出 this 的值如下所示
void Student::printThis(){coutthisendl;
}
然后在 main() 函数中创建对象并调用 printThis()
Student *pstu1 new Student;
pstu1 - printThis();
coutpstu1endl;Student *pstu2 new Student;
pstu2 - printThis();
coutpstu2endl;
运行结果如下可以发现this 确实指向了当前对象而且对于不同的对象this 的值也不一样。
015118E8 015118E8 015118B0 015118B0
使用this指针要注意以下几点
this 是 const 指针它的值是不能被修改的一切企图修改该指针的操作如赋值、递增、递减等都是不允许的。this 只能在成员函数内部使用用在其他地方没有意义也是非法的。只有当对象被创建后 this 才有意义因此不能在 static 成员函数中使用。
总结
成员函数最终会被编译成与对象无关的普通函数所以在编译时成员函数时会给成员函数添加一个额外的参数即当前对象的指针以此来关联成员函数和成员变量。这个额外的参数实际上就是 this 指针它是成员函数和成员变量关联的桥梁。
this 指针实际上是成员函数的一个形参在调用成员函数时将对象的首地址作为实参传递给 this 指针。不过 this 这个形参是隐式的它并不出现在代码中而是在编译阶段由编译器默默地将它添加到参数列表中。
this 指针作为隐式形参本质上是成员函数的局部变量所以只能用在成员函数的内部并且只有在通过对象调用成员函数时才给 this 赋值。