LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

C++面向对象程序设计笔记:第三章 类和对象提高

2022/9/13 C++入门

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

访问静态成员:

  1. 类名::成员名
  2. 对象名.成员名
  3. 指针->成员名
  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,成为常引用。
不能通过常引用修改其引用的值。

友元

  1. 友元函数:一个类的友元函数可以访问该类的私有成员

    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";
    }
    
  2. 友元类:如果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;
}
img_show