C++(2)

第二章. 类和对象

一、什么是对象
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼大虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值