C++面向对象程序设计笔记:第三章 类和对象提高
this指针
易错点:
class A {
int i;
public:
void Hello() {cout << "hello\n"; }
};
int main() {
A* p = NULL;
p->Hello(); // 不会出错
// 输出:hello
}
因为C++在运行之前会转变为C,而成员函数Hello()变为
void Hello(A* this) {cout << "hello\n"; }
如果将友元函数变为以下函数,程序就会出错。
void Hello() {cout << i << "hello\n"; }
静态成员函数中不能使用this指针。
因为静态成员函数并不具体作用于某个对象,因此静态成员函数的真实的参数个数,就是程序中写出的参数个数。其他成员函数的参数中会多出一个this。
静态成员变量
普通成员变量每个对象各有一份,而静态成员变量一共只有一份,为所有对象共享。
普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
因此静态成员不需要通过对象就能访问。
sizeof 运算符不会计算静态成员变量。
class CMyclass {
int n;
static int s;
}
sizeof(CMyclass); // 4
访问静态成员:
- 类名::成员名
- 对象名.成员名
- 指针->成员名
- 引用.成员名
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
静态成员函数本质上是全局函数。
必须在定义类的文件中对静态成员变量进行一次说明或初始化。
否则编译能通过,链接不能通过。
在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
Demo:
class CRectangle {
private:
int w, h;
static int nTotalArea;
static int nTotalNumber;
public:
CRectangle(int w_, int h_);
~CRectangle();
static void PrintTotal();
}
CRectangle::CRectangle(int w_, int h_) {
w = w_;
h = h_;
nTotalNumber++;
nTotalArea += w * h;
}
CRectangle::~CRectangle() {
nTotalNumber--;
nTotalArea -= w * h;
}
void CRectangle::PrintTotal() {
cout << nTotalNumber << "," << nTotalArea << "\n";
}
上述Demo存在一个问题:
在使用CRectangle类时,有时会调用复制构造函数生成临时的隐藏的CRectangle对象
- 调用一个以CRectangle类对象作为参数的函数时
- 调用一个以CRectangle类对象作为返回值的函数时
临时对象在消亡时会调用析构函数,较少nTotalNumber和nTotalArea的值,可是这些临时对象在生成时却没有增加nTotalNumber和nTotalArea的值。
解决办法:为CRectangle类写一个复制构造函数。
CRectangle::CRectangle(CRectangle& c) {
w = r.w; h = r.h;
nTotalNumber++;
nTotalArea += w * h;
}
成员函数和封闭类
- 封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数。
- 对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关。
- 当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。
任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象,是如何初始的:通过封闭类的初始化列表。
常量对象
如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字。
常量成员函数
在类的成员函数说明后面可以加const关键字,则该成员函数称为常量成员函数。
常量成员函数执行期间不应修改其所作用的对象。
因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
class Sample {
public:
int value;
void GetValue() const;
void func() {}
Sample() {}
};
void Sample::GetValue() const {
value = 0; // wrong,不能修改成员变量的值
func(); // wrong,不能调用同类的非常量成员函数
}
int main() {
const Sample o;
o.value = 100; // err,常量对象不可被修改
o.func(); // err,常量对象上面不能执行非常量成员函数
o.GetValue(); // ok,常量对象上可以执行常量成员函数
return 0;
}
两个成员函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
class CTest {
private:
int n;
public:
CTest() {n = 1;}
int GetValue() const { return n;}
int GetValue() { return n;}
};
int main() {
const CTest objTest1;
CTest objTest2;
cout << objTest1.GetValue() << "," << objTest2.GetValue();
return 0;
}
常引用
引用前面可以加const,成为常引用。
不能通过常引用修改其引用的值。
友元
友元函数:一个类的友元函数可以访问该类的私有成员
class CCar; class CDriver { public: void ModifyCar(CCar* pCar); }; class CCar { private: int price; friend int MostExpensiveCar(CCar* pCar); // 声明友元 friend void CDrive::ModifyCar(CCar* pCar); // 声明友元 } void CDrive::ModifyCar(CCar* pCar) { pCar->price += 100; } int MostExpensiveCar(CCar* pCar) { cout << pCar->price << "\n"; }
友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员
class CCar { private: int price; friend class CDrive; // 声明CDriver为友元类 }; class CDriver { public: CCar myCar; void ModifyCar() { myCar.price += 100; } };
注意:友元类之间的关系不能传递,也不能继承。
课后习题
010:返回什么才好呢
#include <iostream>
using namespace std;
class A {
public:
int val;
A(int
x = 123) {
val = x;
}
A& GetObj() {
return *this;
}
};
int main()
{
int m,n;
A a;
cout << a.val << endl;
while(cin >> m >> n) {
a.GetObj() = m;
cout << a.val << endl;
a.GetObj() = A(n);
cout << a.val<< endl;
}
return 0;
}
011:Big & Base 封闭类问题
#include <iostream>
#include <string>
using namespace std;
class Base {
public:
int k;
Base(int n):k(n) { }
};
class Big
{
public:
int v;
Base b;
Big(int x):b(x) {
v = x;
}
};
int main()
{
int n;
while(cin >>n) {
Big a1(n);
Big a2 = a1;
cout << a1.v << "," << a1.b.k << endl;
cout << a2.v << "," << a2.b.k << endl;
}
}
012:这个指针哪来的
#include <iostream>
using namespace std;
struct A
{
int v;
A(int vv):v(vv) { }
const A* getPointer() const {
return this;
}
};
int main()
{
const A a(10);
const A * p = a.getPointer();
cout << p->v << endl;
return 0;
}
013:魔兽世界之一:备战
#include <bits/stdc++.h>
using namespace std;
#define DRAGON 0
#define NINJA 1
#define ICEMAN 2
#define LION 3
#define WOLF 4
int ghours, ghealth;
array<int, 5> gcost;
const void Log(const string out) {
cout << out << "\n";
}
string GetTime() {
string res = to_string(ghours);
while(res.length() < 3) {
res = "0" + res;
}
return res;
}
string GetSoldierName(int id) {
switch (id) {
case 0: return "dragon";
case 1: return "ninja";
case 2: return "iceman";
case 3: return "lion";
case 4: return "wolf";
default: assert(false);
}
return "Undefine";
}
class RedCamp {
private:
int health, pos, total;
bool isEnd;
const array<int, 5> soldier = {
ICEMAN, LION, WOLF, NINJA, DRAGON
};
array<int, 5> number;
public:
RedCamp() {
health = ghealth;
pos = 0; total = 0;
isEnd = false;
for(int i = 0; i < 5; i++) number[i] = 0;
}
bool Active() {
if(isEnd) return false;
int cnt = 0;
for(; cnt < 5; pos = (pos + 1) % 5, cnt++) {
if(gcost[soldier[pos]] > health) continue;
health -= gcost[soldier[pos]];
number[pos]++;
total++;
Log(GetTime() + " red " + GetSoldierName(soldier[pos]) + " " +
to_string(total) + " born with strength " +
to_string(gcost[soldier[pos]]) + "," +
to_string(number[pos]) + " " + GetSoldierName(soldier[pos]) +
" in red headquarter"
);
pos = (pos + 1) % 5;
return true;
}
isEnd = true;
Log(GetTime() + " red headquarter stops making warriors");
return false;
}
};
class BlueCamp {
private:
int health, pos, total;
bool isEnd;
const array<int, 5> soldier = {
LION, DRAGON, NINJA, ICEMAN, WOLF
};
array<int, 5> number;
public:
BlueCamp() {
health = ghealth;
pos = 0; total = 0;
isEnd = false;
for(int i = 0; i < 5; i++) number[i] = 0;
}
bool Active() {
if(isEnd) return false;
int cnt = 0;
for(; cnt < 5; pos = (pos + 1) % 5, cnt++) {
if(gcost[soldier[pos]] > health) continue;
health -= gcost[soldier[pos]];
number[pos]++;
total++;
Log(GetTime() + " blue " + GetSoldierName(soldier[pos]) + " " +
to_string(total) + " born with strength " +
to_string(gcost[soldier[pos]]) + "," +
to_string(number[pos]) + " " + GetSoldierName(soldier[pos]) +
" in blue headquarter"
);
pos = (pos + 1) % 5;
return true;
}
isEnd = true;
Log(GetTime() + " blue headquarter stops making warriors");
return false;
}
};
int main() {
int tests;
cin >> tests;
for(int testCase = 1; testCase <= tests; testCase++) {
Log("Case:" + to_string(testCase));
cin >> ghealth;
for(int i = 0; i < 5; i++) cin >> gcost[i];
RedCamp redCamp;
BlueCamp blueCamp;
for(ghours = 0; ; ghours++) {
int flag = 0;
if(!redCamp.Active()) flag++;
if(!blueCamp.Active()) flag++;
if(flag >= 2) break;
}
}
return 0;
}