第二章. 类和对象
一、什么是对象
1.万物皆对象
2.程序就是一组对象,对象之间通过消息交换信息
3.类就是对对象的描述和抽象,对象就是类的具体化和实例化
二、通过类描述对象
属性:姓名、年龄、学号
行为:吃饭、睡觉、学习
类就是从属性和行为两个方面对对象进行抽象。
三、面向对象程序设计(OOP)
现实世界 虚拟世界
对象 -> 抽象 -> 类 -> 对象
1.至少掌握一种OOP编程语言
2.精通一种面向对象的元语言—UML
3.研究设计模式,GOF
四、类的基本语法
1.类的定义
class 类名 {
};
如
class Student {
};
2.成员变量——属性
class 类名 {
类型 成员变量名;
};
如
class Student {
string m_name;
int m_age;
};
3.成员函数——行为
class 类名 {
返回类型 成员函数名 (形参表) {
函数体;
}
};
如
class Student {
string m_name;
int m_age;
void eat (const string& food) {
...
}
};
4.访问控制属性
1)公有成员:public,谁都可以访问。
2)私有成员:private,只有自己可以访问。
3)保护成员:protected,只有自己和自己的子类可以访问。
4)类的成员缺省访控属性为私有,而结构的成员缺省访控属性为公有。
例子:
#include <iostream>
using namespace std;
class Student {
private: //声明为私有部分
string m_name;
int m_age;
public: //声明为私有部分
void eat (const string& food) {
cout << m_age << "岁的" << m_name
<< "同学正在吃" << food << "。" << endl;
}
void setName (const string& name) { //为接口
if (name == "2")
cout << "你才" << name << "!" << endl;
else
m_name = name;
}
void setAge (int age) { //为接口
if (age < 0)
cout << "无效的年龄!" << endl;
else
m_age = age;
}
};
int main (void) {
Student student;
student.setName ("2"); //你才2
student.setAge (-100); //无效年龄
student.setName ("张飞"); //将其赋值给成员变量m_name
student.setAge (20); //将其赋值给成员变量m_age
student.eat ("包子"); //20岁的张飞同学正在吃包子
return 0;
}
5.构造函数
class {
...
类名 (行参表) {
构造函数体;
}
};
当一个对象被创建时,构造函数会自动被执行,其参数来自构造实参。
6.构造函数可以通过构造参数实现重载。
7.如果一个类没有定义任何构造函数,那么系统就会缺省地为其提供一个无参构造函数,该构造函数对于基本类型的成员变量不做初始化,对于类类型的成员变量,调用其相应类型的无参构造函数初始化。
8.对象创建过程
分配内存->调用构造函数->调用类类型成员的构造函数->构造函数的代码
9.初始化表
class 类名 {
类名(...) :初始化表 {
构造函数体
}
};
const int x = 100;
x = 100;
int& a = b;
a = b;
1)如果类中含有常量或引用型的成员变量,必须通过初始化表对其初始化。
2)成员变量的初始化顺序仅于其被声明的顺序有关,而与初始化表的顺序无关。
class A {
public:
A (char* psz) : m_str (psz),
m_len (m_str.length()) {}
private:
size_t m_len;
string m_str;
}
例子1:类的声明与定义以及使用可以不在一个文件
这是s.h文件
#ifndef _S_H
#define _S_H
#include <string>
using namespace std;
// 声明Student类
class Student {
public:
Student (const string& name = "", int age = 0);
void eat (const string& food);
private:
string m_name;
int m_age;
};
#endif // _S_H
这是s.cpp文件
#include <iostream>
using namespace std;
#include "s.h"
// 实现Student类
Student::Student (const string& name /* = "" */,
int age /* = 0 */) : m_name (name),
m_age (age) {}
void Student::eat (const string& food) {
cout << m_name << "," << m_age << "," << food
<< endl;
}
这是main.cpp文件:
#include "s.h"
// 使用Student类
int main (void) {
Student s ("张飞", 25);
s.eat ("包子");
return 0;
}
例子2:不同的构造函数
#include <iostream>
using namespace std;
class A {
public:
A (int a) {}
};
class Student {
private:
string m_name;
int m_age;
A m_a; //类类型的成员变量
public:
void eat (const string& food) {
cout << m_age << "岁的" << m_name
<< "同学正在吃" << food << "。" << endl;
}
// void _ZN7Student3eatERKSs (Student* this,
// const string& food) {
// cout << this->m_age << "岁的"<<this->m_name
// << "同学正在吃" << food << "。" << endl;
// } //这是计算机中编译的样子
void setName (const string& name) {
if (name == "2")
cout << "你才" << name << "!" << endl;
else
m_name = name;
}
void setAge (int age) {
if (age < 0)
cout << "无效的年龄!" << endl;
else
m_age = age;
}
//如果同时有多种构造函数存在,则根据构造参数来确定调用哪个构造函数,既构造函数可以通过构造参数实现重载
// 构造函数
Student (const string& name, int age) :
m_a (100) {
m_name = name;
m_age = age;
}
// 无参构造
Student (void) : m_a (100) { //没有参数
m_name = "没名";
m_age = 0;
}
// 单参构造
Student (const string& name) : m_a (100),
m_name (name), m_age (0) {
m_name = "哈哈";
}
};
int main (void) {
Student s1 ("张飞", 25);
s1.eat ("包子");
// _ZN7Student3eatERKSs (&s1, "包子"); //编译器中的样子
Student s2 = Student ("赵云", 22);
s2.eat ("烧饼");
// _ZN7Student3eatERKSs (&s2, "烧饼"); //编译器中的样子
Student s3; //调用的无参构造
s3.eat ("煎饼果子");
Student* s4 = new Student ("关羽", 30);//调用有参构造,分配内存,并初始化
s4->eat ("烤鸭"); //当访问地址(指针或迭代器)的成员或数据时,用->
delete s4;
Student& s5 = *new Student ();//调用无参构造初始化
s5.eat ("面条"); //当访问直接对象的成员或数据时,用“.”
delete &s5;
Student sa1[3] = {s1, s2}; //用s1与s2给数组初始化,但第三个元素没有赋值
sa1[0].eat ("KFC"); //25岁的张飞同学正在吃KFC
sa1[1].eat ("KFC"); //22岁的赵云同学正在吃KFC
sa1[2].eat ("KFC"); //0岁的无名同学正在吃KFC
Student* sa2 = new Student[3] {s1, s2}; // c++2011支持
sa2[0].eat ("KFC");
sa2[1].eat ("KFC");
sa2[2].eat ("KFC");
delete[] sa2;
Student s6 ("刘备");//调用单参构造
s6.eat ("米饭");//
return 0;
}
练习: 实现一个Clock类支持两种工作模式,计时器和电子钟。
00:01:00
14:05:37
#include
cout << setw(4) << setfill(‘0’) << 12;
0012
Clock
时、分、秒
走 - 显示、滴答
练习答案:
#include <iostream>
#include <iomanip>
using namespace std;
class Clock {
public:
Clock (bool timer = true) :
m_hour (0), m_min (0), m_sec (0) {
if (! timer) {
time_t t = time (NULL);
tm* local = localtime (&t);
m_hour = local->tm_hour;
m_min = local->tm_min;
m_sec = local->tm_sec;
}
}
void run (void) {
for (;;) {
show ();
tick ();
}
}
private:
void show (void) {
cout << '\r' << setfill ('0')
<< setw (2) << m_hour << ':'
<< setw (2) << m_min << ':'
<< setw (2) << m_sec << flush;
// printf ("\r%02d:%02d:%02d", m_hour,
// m_min, m_sec);
}
void tick (void) {
sleep (1);
if (++m_sec == 60) {
m_sec = 0;
if (++m_min == 60) {
m_min = 0;
if (++m_hour == 24)
m_hour = 0;
}
}
}
int m_hour;
int m_min;
int m_sec;
};
int main (void) {
Clock clock (false);
clock.run ();
return 0;
}
五、this指针
1.一般而言,在类的构造函数或成员函数中,关键字this表示一个指针,对于构造函数而言,this指向正在被构造的对象,对于成员函数而言,this指向调用该函数的对象。
2.this指针的用途
1)在类的内部区分成员变量。
2)在成员函数中返回调用对象自身。
3)在成员函数内部通过参数向外界传递调用对象自身,以实现对象间交互。
老 -问题-> 学
师 <-答案- 生
class A {
B m_b;
};
class B {
A m_a;
};
sizeof(A) //error
class C {
C m_c;
};
例子1:
#include <iostream>
using namespace std;
class A {
public:
A (int data) : data (data) {
cout << "构造: " << this << endl;
// this->data = data;
}
void foo (void) {
cout << "foo: " << this << endl;
cout << this->data << endl;
}
int data;
};
int main (void) {
A a (1000); //创建对象调用了构造函数,并输出this的地址,输出“构造:0xbf9b24d8”
cout << "main: " << &a << endl;//输出该对象的地址,输出“main:0xbf9b24d8”
a.foo (); //该对象调用foo函数,输出this的值,以及输出this->data的值,输出“foo:0xbf9b24d8 1000”
A* pa = new A (1000); //创建对象调用构造函数,输出this的地址,输出“构造:0x95cc008”
cout << "main: " << pa << endl; //输出该对象的地址,输出“main:0x95cc008”
pa->foo (); //该对象调用foo函数,输出this以及this->data的值,输出“foo:0x95cc008 1000”
delete pa;
}
例子2:
#include <iostream>
using namespace std;
class Counter {
public:
Counter (void) : m_data (0) {}
Counter& inc (void) { //返回的是一个别名,不加&的话,返回的就是一个拷贝
++m_data;
return *this;
}
void print (void) {
cout << m_data << endl;
}
private:
int m_data;
};
int main (void) {
Counter c;
// c.inc ();
// c.inc ();
// c.inc ();
c.inc ().inc ().inc ();//函数返回的是一个别名,是一个左值,可以用来调用函数
c.print (); // 输出为3,如果前面的函数不加&,返回的只是拷贝,输出为1。
return 0;
}
例子3:学生与老师
#include <iostream>
using namespace std;
class Student; //因为在Teacher中会用到Student,所以提前声明一下
class Teacher {
public:
void educate (Student* s);//可以声明在类的内部,定义在类的外部
void reply (const string& answer) {
m_answer = answer;
}
private:
string m_answer;
};
class Student {
public:
void ask (const string& question, Teacher* t) {
cout << "问题:" << question << endl;
t->reply ("不知道。");
}
};
void Teacher::educate (Student* s) {
s->ask ("什么是this指针?", this);//将问题question和Teacher类变量的地址作为参数传递给Student类中的ask成员函数,并在ask函数中得到一个值作为参数传递给Teacher类中的replay函数,将值赋给m_answer,最后完成输出。
cout << "答案:" << m_answer << endl;
}
int main (void) {
Teacher t;
Student s;
t.educate (&s);
return 0;
}
六、常函数与常对象
1.如果在一个类的成员函数的参数表后面加上const关键字,那么这个成员函数就被成为常函数,常函数的this指针是一个常指针。在常函数内部无法修改成员变量,除非该变量具有mutable属性。而且在常函数内部也无法调用非常函数。
2.常对象:拥有const属性的对象,兑现引用或指针。
常对象只能调用常函数。
同型的常函数和非常函数可以构成重载关系。常对象调用常版本,非常对象调用非常版本。如果没有非常版本,非常对象也可以调用常版本。
const XXX 函数名 (const YYY yyy)
const {
...
}
例子:
#include <iostream>
using namespace std;
class A {
public:
// void bar (void) { //函数1
// cout << "非常bar" << endl;
// }
//函数1与函数2构成函数重载
void bar (void) const { //函数2
cout << "常bar" << endl;
}
// void XXXbarYYY (A* this) {}
void foo (void) const { //函数3
// m_i = 100; //在常函数内部无法修改成员变量,除非那个成员变量有mutable属性,例:mutable int m_i;
const_cast<A*>(this)->m_i = 100;//也可以通过去常来解决
}
void print (void) const {
cout << m_i << endl;
}
// _ZNK1A3fooEv (const A* this) {
// const_cast<A*>(this)->m_i = 100;
// }
int m_i;
};
void func (void) /*const*/ {}
int main (void) {
A a;
a.foo (); //调用的是函数3
a.print ();//“100”
const A& r = a;//r为常对象,a为非常对象
r.bar ();//“常bar”,r为常对象,常对象只能调用常函数
// XXXbarYYY (&r); // const A*
a.bar ();//“常bar”,a为非常对象,可以调用常函数,也可调用非常函数
// XXXbarYYY (&a); // A*
return 0;
}
七、析构函数
class 类名 {
~类名 (void) {
析构函数体;
}
};
当一个对象被销毁时自动执行析构函数。
局部对象离开作用域时被销毁,堆对象被delete时被销毁。
如果一个类没有定义任何析构函数,那么系统会提供一个缺省析构函数。缺省析构函数对基本类型的成员变量什么也不干,对类类型的成员变量,调用相应类型的析构函数。
一般情况下,在析构函数中释放各种动态分配的资源。
构造: 基类->成员->子类
析构: 子类->成员->基类
例子:
#include <iostream>
using namespace std;
class Double {
public:
Double (double data) :
m_data (new double (data)) {
cout << "构造" << endl;
} //构造函数早开始时执行,既创建对象时执行
~Double (void) {
cout << "析构" << endl;
delete m_data;
}//析构函数在对象结束时执行
void print (void) const {
cout << *m_data << endl;
}
private:
double* m_data;
string m_lable;
};
int main (void) {
// {
Double d1 (3.14);
d1.print ();//“构造,3.14”(d1调用的构造)
// }
Double* d2 = new Double (1.23);//“构造”(d2调用的构造)
delete d2; //“析构”(d2调用的析构)
cout << "再见!" << endl;//“再见”
return 0; //“析构”,(程序结束,d1调用的析构)
}
-----------------------------------------------------------------------------------八、拷贝构造函数和拷贝赋值运算符
1.拷贝构造:用一个已有的对象,构造和它同类型的副本对象——克隆。
2.拷贝构造函数
形如
class X {
X (const X& that) { ... }
};
的构造函数成为拷贝构造函数。如果一个类没有定义拷贝构造函数,系统会提供一个缺省拷贝构造函数。缺省拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝构造函数。
3.在某些情况就下,缺省拷贝构造函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝构造函数。
例子:拷贝函数
#include <iostream>
using namespace std;
class Integer {
public:
Integer (int data = 0) : m_data (data) {}//构造函数
void print (void) const {
cout << m_data << endl;
}
//拷贝构造(自己定义的):
Integer (const Integer& that) :
m_data (that.m_data) {
cout << "拷贝构造" << endl;
}
private:
int m_data;
};
void foo (Integer i) { //用Inerger类变量时实参给函数中Interger类的形参赋值,同样会调用拷贝构造函数
i.print ();
}
Integer bar (void) {
Integer i;
return i;
}
int main (void) {
Integer i1 (10);
i1.print ();//正常创建对象,输出“10”
Integer i2 (i1); // 调用拷贝构造,输出“拷贝构造”
i2.print (); //调用print函数,输出“10”
Integer i3 = i1; // 调用拷贝构造,输出“拷贝构造”
i3.print (); //调用print函数,输出“10”
// Integer i4 (10, 20);
cout << "调用foo()函数" << endl;
foo (i1); //调用拷贝构造函数,且调用print函数输出,所以输出为“拷贝构造 10”
cout << "调用bar()函数" << endl;
Integer i4 (bar ());
return 0;
}
4.拷贝赋值运算符函数
形如
class X {
X& operator= (const X& that) {
...
}
};
的成员函数称为拷贝赋值运算符函数。如果一个类没有定义拷贝赋值运算符函数,系统会提供一个缺省拷贝赋值运算符函数。缺省拷贝赋值运算符函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝赋值运算符函数。
5.在某些情况就下,缺省拷贝赋值运算符函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝赋值运算符函数。
例子:拷贝赋值运算符函数
#include <iostream>
using namespace std;
class Integer {
public:
Integer (int data) : m_data (new int (data)) {}
//构造函数
~Integer (void) { //析构函数
if (m_data) {
delete m_data;
m_data = NULL;
}
}
void print (void) const {
cout << *m_data << endl;
}
Integer (const Integer& that) : //拷贝构造函数
m_data (new int (*that.m_data)) {}
void set (int data) {
*m_data = data;
}
//拷贝赋值运算符函数(运算符重载)
Integer& operator= (const Integer& that) {
// 防止自赋值
if (&that != this) {
// 释放旧资源
delete m_data;
// 分配新资源
m_data = new int (*that.m_data);
// 拷贝新数据
}
// 返回自引用
return *this;
}
private:
int* m_data;
};
int main (void) {
Integer i1 (10);
i1.print ();
Integer i2 (i1);
i2.print ();
i2.set (20);
i2.print ();
i1.print ();
Integer i3 (30);
i3.print (); // 30
i3 = i1; // 拷贝赋值
// i3.operator= (i1);
i3.print (); // 10
i3.set (40);
i3.print (); // 40
i1.print (); // 10
/*
int a = 10, b = 20, c = 30;
(a = b) = c;
cout << a << endl;
*/
(i3 = i1) = i2;
// i3.operator=(i1).operator=(i2);
i3.print ();
i3 = i3;
i3.print ();
return 0;
}
练习:实现一个简化版的字符串类String支持:
1.用字符指针形式的字符串构造
2.拷贝构造和拷贝赋值
3.获取字符指针形式字符串的成员函数,类似string::c_str
说明:不得使用std::string!
练习答案:
#include <iostream>
#include <cstring>
using namespace std;
class String {
public:
String (const char* str = NULL) { //构造函数
m_str = new char[strlen(str?str:"")+1];
strcpy (m_str, str ? str : "");
}
~String (void) { //析构函数
if (m_str) {
delete[] m_str;
m_str = NULL;
}
}
String (const String& that) : //拷贝构造函数
m_str (strcpy (
new char[strlen(that.m_str)+1],
that.m_str)) {}
/* 菜鸟
void operator= (const String& that) {
m_str = new char[strlen(that.m_str)+1];
strcpy (m_str, that.m_str);
}*/
String& operator= (const String& that) {
if (&that != this) {
/* 小鸟
delete[] m_str;
m_str = new char[strlen(that.m_str)+1];
strcpy (m_str, that.m_str);
*/
/* 大鸟
char* str =
new char[strlen(that.m_str)+1];
delete[] m_str;
m_str = strcpy (str, that.m_str);
*/
// 老鸟
String temp (that);
swap (m_str, temp.m_str);
}
return *this;
}
const char* c_str (void) const {
return m_str;
}
private:
char* m_str;
};
int main (void) {
String s1 ("Hello, World !");
cout << s1.c_str () << endl;
String s2 = s1;
cout << s2.c_str () << endl;
String s3 ("Hello, Linux !");
try {
s1 = s3;
}
catch (exception& ex) {
cout << ex.what () << endl;
}
cout << s1.c_str () << endl;
return 0; }
九、静态成员
1.静态成员变量和静态成员函数是属于类的而非属于对象。
2.静态成员变量,为多个对象所共享,只有一份实例,可以通过对象访问也可以通过类访问,必须在类的外部定义并初始化。静态成员变量本质上与全局变量并没有区别,只是多了类作用域的约束,和访控属性的限制。
class Account {
private:
string m_name;
double m_balance;
static double m_rate;
};
3.静态成员函数,没有this指针,无法访问非静态成员。
4.单例模式
例子0:
#include <iostream>
using namespace std;
class A {
public:
static int m_i;
static void foo (void) {
cout << "foo:" << m_i << endl;
// m_d = 3.14;//报错,静态成员函数不能访问非静态成员成员
// bar (); //报错,理由同上
}
double m_d;
void bar (void) {
m_i = 1000;//OK,非静态成员函数可以访问非静态成员,也可以访问静态成员
foo (); //OK,理由同上
}
};
int A::m_i = 1;//在外部定义
int main (void) {
A::m_i = 10;//通过类访问静态成员变量
A a1, a2;
cout << ++a1.m_i << endl;//通过对象访问静态成员变量,输出为“11”
cout << a2.m_i << endl; //因为静态成员变量,为多个对象共享,只有一个实例,所以上面a1将m_i修改为11,则通过a2访问的m_i也是11,输出为“11”
A::foo ();//输出为“foo:11”,通过类访问静态成员函数
a1.foo ();//输出为“foo:11”,通过对象访问静态成员函数
a1.bar ();//先调用bar,将m_i修改为1000,再调用foo,输出为“foo:1000”
return 0;
}
例子1:单例模式(饿汉方式):
#include <iostream>
using namespace std;
// 饿汉方式
class Singleton {
public:
static Singleton& getInst (void) {
return s_inst; //当调用这个函数时,不会新创建对象,不会调用构造函数,只是返回创建好的那个对象
}
private:
Singleton (void) {} //构造函数
Singleton (const Singleton&);//拷贝构造函数
static Singleton s_inst; //(类的内部)静态成员变量的声明,所有对象公用
};
Singleton Singleton::s_inst;//(类的外部)静态成员变量的定义,这个时候已经创建了对象,并调用构造函数
int main (void) {
Singleton& s1 = Singleton::getInst ();//不会创建新的对象,返回已经创建好的Singletion类对象,并没有新分配内存和地址
Singleton& s2 = Singleton::getInst ();//与上面一样,还是返回那个对象,内存地址没有变
Singleton& s3 = Singleton::getInst ();//还是一样滴
cout << &s1 << ' ' << &s2 << ' ' << &s3 << endl;
//输出的都是0x804a0d4
return 0;
}
例子2:单例模式(懒汉模式)
#include <iostream>
using namespace std;
// 懒汉方式
class Singleton {
public:
static Singleton& getInst (void) {
if (! m_inst) //m_inst指针被初始化为NULL,第一次调用时,执行下面那一行代码来分配内存创建一个对象。否则跳过那一行代码。一旦执行过一次下一行代码,则m_inst就不会再为NULL,也就是说只会分配一次内存,只有一个对象
m_inst = new Singleton;//分配内存,创建对象,执行一次
++m_cn;//调用一次该函数,则数量加一
return *m_inst;//返回创建好的m_inst
}
void releaseInst (void) {
if (m_cn && --m_cn == 0)//调用了几次getInst,就要调用几次该函数,才能真正把对象释放掉
delete this;
}
private:
Singleton (void) { //构造函数
cout << "构造:" << this << endl;
}
Singleton (const Singleton&);//析构函数
~Singleton (void) {
cout << "析构:" << this << endl;
m_inst = NULL;
}
static Singleton* m_inst;//声明该指针(静态)
static unsigned int m_cn;//声明该变量(静态)
};
Singleton* Singleton::m_inst = NULL;//先将指针初始化,并没有创建对象,分配内存
unsigned int Singleton::m_cn = 0;//创建对象并初始化,调用构造,这个变量什维利计算m_inst的数量
int main (void) {
Singleton& s1 = Singleton::getInst ();//第一次调用getInst函数,所以执行哪一行代码来分配内存创建对象,并返回该对象
Singleton& s2 = Singleton::getInst ();//因为不是第一次调用,m_inst已经不再指向NULL,所以根据条件语句,不执行创建对象的代码,直接返回原先第一次调用还函数时创建好的对象,地址神马的都不变
Singleton& s3 = Singleton::getInst ();//同上
cout << &s1 << ' ' << &s2 << ' ' << &s3 << endl;
//输出的地址都是一样的,因为只分配了一次内存,创建了一次对象
s3.releaseInst ();//调用时,m_cn为3,结束时为2,没有释放掉
s2.releaseInst ();//调用时,m_cn为2,结束时为1,没有释放掉
s1.releaseInst ();//调用时,m_cn为1,满足条件语句,执行delete this ,真正释放掉
return 0;
}
十、成员指针
Student s;
string* p = &s.m_name; // 不是
Student s2;
(一)成员变量指针
1.定义
成员变量类型 类名::*指针变量名;
string Student::*pname;
int Student::*page;
2.初始化/赋值
指针变量名 = &类名::成员变量名
pname = &Student::m_name;
page = &Student::m_age;
3.解引用
对象.*指针变量名
对象指针->*指针变量名
Student s, *p = &s;
s.*pname = "张飞";
cout << p->*page << endl;
(二)成员函数指针
1.定义
成员函数返回类型 (类名::*指针变量名) (参数表)
void (Student::*plearn) (const string&) const;
2.初始化/赋值
指针变量名 = &类名::成员函数名;
plearn = &Stduent::learn;
3.解引用
(对象.*指针变量名) (实参表);
(对象指针->*指针变量名) (实参表);
(s.*plearn) ("C++");
(p->*plearn) ("UNIX");
例子:
#include <iostream>
#include <cstring>
using namespace std;
class Student {
public:
Student (const string& name, int age) : //构造函数
m_name (name), m_age (age) {}
double m_weight;
string m_name;
Int m_age;
void learn (const string& lesson) const {
cout << "我在学" << lesson << endl;
}
static void hello (void) {
cout << "你好!" << endl;
}
};
int main (void) {
//成员变量指针使用对象的地址作为基础,然后用成员变量的偏移量得到该对象中的成员变量的绝对地址。
string Student::*pname = &Student::m_name;//声明一个指向Student类中m_name成员变量的成员变量指针
void* pv;
memcpy (&pv, &pname, 4);
cout << pv << endl;//输出“0x8”
int Student::*page = &Student::m_age; //声明一个指向Student类中m_age成员变量的成员变量指针
memcpy (&pv, &page, 4);
cout << pv << endl;//输出“0xc”
Student s ("张飞", 25), *p = &s;//p是指向对象的指针。存的是对象s的地址
cout << s.*pname << endl;//成员变量指针,输出“张飞”
cout << p->*page << endl;//成员变量指针,输出“25”
Student s2 ("赵云", 22);
cout << s2.*pname << endl;//输出“赵云”
void (Student::*plearn) (const string&) const =
&Student::learn;//成员函数指针
(s.*plearn) ("C++");//对象是用地址提供this的实参,输出“我在学c++”
(p->*plearn) ("UNIX");//输出“我在学UNIX”
void (*phello) (void) = Student::hello;
phello ();//静态的函数就不需要用对象的地址来提供this的实参,输出“你好”
return 0;
}