[C++ Primer Plus] Chapter 10 객체와 클래스

가장 중요한 OOP 기능: 추상화, 캡슐화 및 데이터 은닉, 다형성, 상속, 코드 재사용성.

10.1 절차적 프로그래밍 OPP와 객체지향 프로그래밍 OOP

  1. 절차적 프로그래밍 접근 방식을 취할 때 먼저 따라야 할 단계를 고려한 다음 이 데이터를 나타내는 방법을 고려하십시오.
  2. OOP 접근 방식을 채택할 때 개체는 먼저 사용자의 관점에서 고려됩니다. 개체를 설명하는 데 필요한 데이터와 데이터와 사용자의 상호 작용을 설명하는 데 필요한 작업입니다. 인터페이스를 설명한 후에는 인터페이스와 데이터 저장소를 구현하는 방법을 결정해야 합니다. 마지막으로 새로운 디자인을 사용하여 프로그램이 생성됩니다.

10.2 추상화와 클래스

추상화는 사용자 정의 유형에 대한 바로 가기입니다.C++에서 사용자 정의 유형은 추상 인터페이스를 구현하는 클래스 설계를 나타냅니다.
클래스는 추상화를 사용자 정의 유형으로 변환하고 데이터 표현과 데이터를 깔끔한 패키지로 조작하는 방법을 결합하는 C++ 도구입니다.

주식을 나타내는 클래스:
조작 방법: 주식 취득, 보유량 증가, 주식 매도, 주가 업데이트, 보유량에 대한 정보 표시.
데이터 표현: 회사 이름, 보유 주식 수, 주당 가격, 총 주식 가치.

일반적으로 클래스 사양은 두 부분으로 구성됩니다.

  1. 클래스 선언: 데이터 부분을 데이터 멤버 형식으로 설명하고 공용 인터페이스를 멤버 함수(메소드라고 함) 형식으로 설명합니다.
  2. 클래스 메서드 정의: 클래스 멤버 함수(클래스의 인터페이스)를 구현하는 방법을 설명합니다.

멤버 함수의 클래스 선언 및 정의:

  1. 일반적으로 C++ 프로그래머는 헤더 파일에 인터페이스(클래스 정의)를 배치하고 소스 코드 파일에 구현(클래스 메소드용 코드)을 배치합니다.
  2. 헤더 파일에서 일반적이지만 보편적이지 않은 규칙은 클래스 이름의 첫 글자를 대문자로 하는 것입니다.
  3. 헤더 파일에서 데이터 항목은 일반적으로 비공개 부분 private:(이 키워드는 생략 가능)에 배치되고 클래스 인터페이스를 구성하는 멤버 함수는 공개 부분에 배치됩니다.public:
  4. 헤더 파일에서 함수가 호출 개체를 수정하지 않도록 함수의 괄호 뒤에 const 키워드를 넣습니다.
  5. 클래스 개체를 사용하는 프로그램은 공용 부분에 직접 액세스할 수 있지만 공용 멤버 함수(또는 friend 함수)를 통해서만 개체의 전용 멤버에 액세스할 수 있습니다.
  6. 멤버 함수를 정의할 때 범위 결정 연산자(::)를 사용하여 함수가 속한 클래스를 식별합니다.void Stock::update(double price)
  7. 클래스 멤버 함수(메서드)는 클래스 개체를 통해 호출할 수 있습니다. 이렇게 하려면 멤버십 연산자 마침표(.)를 사용해야 합니다.
  8. 헤더 파일에서 정의가 클래스 선언에 있는 함수는 자동으로 인라인됩니다.
  9. 헤더 파일에서 클래스 선언 외부에 멤버 함수를 정의하고 인라인을 추가하면 인라인 함수라고 할 수도 있습니다. 이때 함수 ​​이름에도 (::)를 추가해야 합니다.
  10. 동일한 유형의 다른 개체에 개체를 할당할 수 있습니다.
  11. 클래스 및 구조체: C++ 프로그래머는 일반적으로 클래스를 사용하여 클래스 설명을 구현하고 순수한 데이터 개체를 나타내도록 구조체를 제한합니다.

클래스 설계를 지정하는 첫 번째 단계: 클래스 선언 제공

  1. 클래스 선언은 구조체 선언과 유사하며 데이터 멤버와 함수 멤버를 포함할 수 있습니다.
  2. 선언에는 선언된 멤버가 멤버 함수를 통해서만 액세스할 수 있는 비공개 부분이 있습니다.
  3. 데이터를 비공개 부분으로 캡슐화하면 데이터 은닉으로 알려진 데이터의 무결성이 보호됩니다.
  4. 선언에는 클래스 개체를 사용하는 프로그램에서 선언된 멤버에 직접 액세스할 수 있는 공개 섹션도 있습니다.
  5. 공개 부분의 내용은 디자인의 추상 부분인 공개 인터페이스를 구성합니다.

stock00.h // 클래스 선언 헤더 파일

// 类的声明:类的变量和成员函数,数据和操纵数据的方法
#ifndef PRIMERPLUS_STOCK00_H
#define PRIMERPLUS_STOCK00_H
#include <string>
class Stock // 首字母一般大写
{
    
    
private:    // 这个关键字可以省略,防止其他源代码访问里面的数据,凡是在private里面的数据,只有public里面的方法可以调用。
    std::string company;    // 成员
    long shares;
    double share_val;
    double total_val;
    void set_total() {
    
    total_val = shares * share_val;} // 内联函数
public:     // 这个关键字不能省略

    Stock();    // 默认构造函数,在声明和定义时不加形参,但是定义时给每个成员初始化
                // 函数重载,自定义构造函数,没有返回值,部分形参使用默认参数
    Stock(const std::string &co, long n = 1, double pr = 1.0);
    ~Stock();                           // 析构函数的声明
    void buy(long num, double price);   // 成员函数
    void sell(long num, double price);
    void update(double price);
    void show() const;                  // const成员函数,保证函数不会修改调用对象
    const Stock & topval(const Stock &s) const;
	const string &company_name() const {
    
    return company;}	// 返回公司的名字且不希望被修改
};
#endif //PRIMERPLUS_STOCK00_H

클래스 설계 지정의 두 번째 단계: 클래스 멤버 함수 구현

  1. 함수 프로토타입 대신 클래스 선언에 전체 함수 정의를 제공할 수 있지만 함수 정의를 자체적으로 제공하는 것이 일반적입니다(함수가 작지 않은 경우).
  2. 이 경우 멤버 함수가 속한 클래스를 나타내기 위해 범위 결정 연산자(::)를 사용해야 합니다.

stock00.cpp // 클래스 정의 소스 파일

// 定义类的成员函数
#include <iostream>
#include "stock00.h"
using namespace std;
Stock::Stock()  // 默认构造函数
{
    
    
    company = "stock";
    shares = 0;
    share_val = 0.0;
    set_total();
}

Stock::Stock(const std::string &co, long n, double pr)  // 自定义构造函数
{
    
    
    company = co;
    if (n<0)
    {
    
    
        cout << "Number of shares can't be negative; "
             << company << " shares set to be 0." << endl;
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_total();
}

Stock::~Stock() // 析构函数定义,没有参数,没有返回值,自动调用
{
    
    
    cout << "Bye " << company << endl;  // 类储存是以栈的方式:先进后出,后进先出
}

void Stock::buy(long num, double price)
{
    
    
    if (num < 0)
        cout << "Number of shares can't be negative; " << endl;
    else
    {
    
    
        shares += num;
        share_val = price;
        set_total();
    }
}

void Stock::sell(long num, double price)
{
    
    
    if (num < 0)
        cout << "Number of shares can't be negative; " << endl;
    else if (num > shares)
        cout << "You can't sell more than you have!" << endl;
    else
    {
    
    
        shares -=num;
        share_val = price;
        set_total();
    }
}

void Stock::update(double price)
{
    
    
    share_val = price;
    set_total();
}

void Stock::show() const
{
    
    
    cout << "Company : " << company << endl;
    cout << "Shares : " << shares << endl;
    cout << "Share price : " << share_val << endl;
    cout << "Total worth : " << total_val << endl;
}

const Stock & Stock::topval(const Stock &s) const
{
    
    
    if (s.total_val > total_val)    // total = this->total_val
        return s;
    else
        return *this;   // this指针指向调用成员函数的对象,this是该对象的地址。
}

클래스 설계 지정의 세 번째 단계: 클래스 객체 생성 및 클래스 메서드 사용

  1. 객체(클래스의 인스턴스)를 만들려면 클래스 이름을 유형 이름으로 취급하면 됩니다.
  2. 클래스 멤버 함수(메서드)는 클래스 개체를 통해 호출할 수 있습니다. 이렇게 하려면 멤버십 연산자 마침표(.)를 사용해야 합니다.

usestock00.cpp // 클래스 사용 소스 파일

#include <iostream>
#include "stock00.h"
using namespace std;
int main(void)
{
    
    
    {
    
    
        Stock wind1 = Stock{
    
    "wind1"};       // 显示调用构造函数
        Stock wind2{
    
    "wind2", 20, 20.2};     // 隐式调用构造函数
        Stock wind3;                        // 自动调用默认构造函数
        wind3 = wind2;                      // 可以将一个对象赋给同类型的另一个对象
        wind3 = Stock("wind3", 30, 30.3);   // 构造函数可对已存在的对象赋值
        const Stock wind4 = Stock{
    
    "wind4"}; // wind4只能调用const成员函数,show()

        Stock top;
        top = wind1.topval(wind2);
        top.show();

        const int STKS = 4;
        Stock mystuff[STKS];                // 创建对象数组,自动调用默认构造函数
        Stock sand[STKS] = {
    
                    // 创建对象数组,对不同的元素使用不同的构造函数
                Stock("sand1", 11, 11.1),   // 用括号括起,以逗号分割
                Stock(),                    // 使用默认构造函数
                Stock("sand3", 33)          // 只初始化了三个元素,剩下的自动调用默认构造函数
        };
        int i;
        for (i=0; i<STKS; i++)
            sand[i].show();
        const Stock *topsand = &sand[0];    // 定义一个类的const指针
        for (i=1; i<STKS; i++)
            topsand = &(topsand->topval(sand[i]));
        topsand->show();                    // 显示价格最高的一个对象

//        Stock fluffy_the_cat;
//        fluffy_the_cat.acquire("Mooncake", 20, 12.5);
//        fluffy_the_cat.show();    		// 通过类的对象来访问类的公有成员函数
//        fluffy_the_cat.buy(15, 18.125);
//        fluffy_the_cat.show();
//        fluffy_the_cat.sell(400, 20.0);
//        fluffy_the_cat.update(15.2);
//        fluffy_the_cat.show();

    }   // 添加这个大括号后,析构函数调用将在到达返回语句前执行。
    return 0;
}

10.3 클래스 생성자와 소멸자

10.3.1 생성자

왜 생성자가 필요한가요? 데이터 섹션의 액세스 상태는 비공개입니다. 즉, 프로그램이 데이터 멤버에 직접 액세스할 수 없습니다. 프로그램은 멤버 함수를 통해서만 데이터 멤버에 액세스할 수 있으므로 개체를 성공적으로 초기화하려면 적절한 멤버 함수를 설계해야 합니다. C++는 새로운 객체를 구성하고 데이터 멤버에 값을 할당하는 데 특별히 사용되는 클래스 생성자라는 특수 멤버 함수를 제공합니다.

  1. 객체가 클래스로 생성되면 생성자가 자동으로 호출됩니다.
  2. 생성자의 함수 이름은 클래스 이름과 동일합니다. 함수 오버로딩을 통해 동일한 이름으로 여러 생성자를 생성할 수 있습니다.
  3. 하나의 매개변수를 사용하는 생성자는 할당 구문을 사용하여 개체를 값으로 초기화할 수 있습니다.
  4. 생성자의 프로토타입 및 함수 헤더에는 흥미로운 기능이 있습니다. 값을 반환하지는 않지만 void로 선언되지는 않습니다. 실제로 생성자는 유형을 선언하지 않습니다.
  5. 생성자의 매개 변수는 클래스 멤버가 아니라 클래스 멤버에 할당된 값을 나타냅니다. 따라서 매개변수 이름은 클래스 멤버와 같을 수 없습니다.
  6. 생성자가 개체를 구성할 때까지 개체가 존재하지 않기 때문에 개체를 사용하여 생성자를 호출할 수 없습니다. 따라서 생성자는 객체를 생성하는 데 사용되며 객체를 통해 호출할 수 없습니다.
  7. 기본 구성 매개변수는 형식 매개변수(있는 경우 모두 초기화된 기본 매개변수가 있어야 함)를 가질 수 있거나 매개변수가 없을 수 있습니다.
// 默认构造函数,在定义时不加形参,但是给每个成员初始化
Stock();   	
Stock::Stock() {
    
    ...}	// Stock类的默认构造函数的定义,没有形参,但是给每个成员初始化

// 函数重载,自定义构造函数,没有返回值,部分形参使用默认参数
Stock(const std::string &co, long n=1, double pr=1.0); 	
Stock::Stock(const std::string &co, long n, double pr) {
    
    ...} // Stock类的构造函数的定义
    
Stock fluffy_the_cat = Stock{
    
    "Mooncake"};   // 显示调用自定义构造函数
Stock garment{
    
    "apple", 30, 123.45};         // 隐式调用自定义构造函数
Stock first;                                // 自动调用默认构造函数

10.3.3 소멸자

  1. 객체가 생성자로 생성된 후 객체가 만료될 때까지 추적하는 것은 프로그램의 책임입니다. 개체가 만료되면 프로그램은 자동으로 특수 멤버 함수인 소멸자를 호출합니다.
  2. 소멸자는 정리를 수행합니다. 생성자가 new를 사용하여 메모리를 할당하면 소멸자는 해당 메모리를 해제하기 위해 delete를 사용합니다.
  3. 소멸자의 원형: 이름 앞에 ~가 있는 클래스 이름이 옵니다. 반환 값과 선언된 형식이 없습니다. 매개변수가 없습니다. Stock 클래스 소멸자의 프로토타입~Stock();
  4. 프로그래머가 소멸자를 제공하지 않으면 컴파일러는 암시적으로 기본 소멸자를 선언하고 개체를 삭제하는 코드를 찾은 후 기본 소멸자의 정의를 제공합니다.
  5. 클래스의 자동 변수 저장은 스택 형태로 소멸자가 호출되어 해제되면 선입선출(First-in, last-out, last-in-first-out)이다.
  6. 생성자가 new를 사용하는 경우 delete를 사용하는 소멸자를 제공해야 합니다.

소멸자가 호출될 때:

  1. 정적 스토리지 클래스 객체가 생성되면 해당 소멸자가 프로그램 종료 시 자동으로 호출됩니다.
  2. 자동 스토리지 클래스 개체가 생성된 경우(이전 예제에서와 같이) 프로그램이 개체가 정의된 코드 블록의 실행을 완료하면 해당 소멸자가 자동으로 호출됩니다.
  3. 객체가 new를 통해 생성되면 스택 메모리 또는 여유 저장소에 상주하며 메모리를 해제하기 위해 delete를 사용할 때 해당 소멸자가 자동으로 호출됩니다.
  4. 마지막으로 프로그램은 특정 작업을 수행하기 위해 임시 개체를 만들 수 있습니다. 이 경우 프로그램은 개체 사용을 마치면 소멸자를 자동으로 호출합니다.

10.4 this 포인터

this 포인터는 멤버 함수를 호출하는 개체를 가리키며 this는 개체의 주소입니다.

두 개체 중 total_val 값이 더 큰 개체를 비교하고 더 큰 개체를 반환합니다.

  1. 멤버 함수와 비교할 두 개체를 어떻게 제공합니까? 메서드가 두 개체를 비교하도록 하려면 두 번째 개체를 인수로 전달해야 합니다. 효율성을 위해 참조로 매개변수를 전달할 수 있습니다.
  2. 메서드의 응답을 호출 프로그램에 어떻게 다시 전달합니까? 가장 간단한 방법은 메서드가 주식 가격의 총 가치가 더 높은 개체에 대한 참조를 반환하도록 하는 것입니다.

함수 프로토타입: const Stock & topval(const Stock & s) const;
함수 호출:top = stock1.topval(stock2); // top도 객체
함수 정의입니다.

const Stock & Stock::topval(const Stock & s) const
{
    
    
	if (s.total_val > total_val)	// total = this->total_val
        return s;		// argument object
	else
		return *this; 	// this 指针指向调用对象
}
  1. 이 함수는 하나의 개체 stock1과 다른 개체 stock2에 암시적으로 액세스하고 개체 중 하나에 대한 참조를 반환합니다.
  2. 괄호 안의 const는 이 함수가 명시적으로 액세스된 개체 stock2를 수정하지 않음을 나타냅니다.
  3. 괄호 뒤의 const는 함수가 암시적으로 액세스되는 개체 stock1을 수정하지 않음을 나타냅니다.
  4. 함수는 두 개의 const 개체 중 하나에 대한 참조를 반환하므로 반환 형식도 const 참조여야 합니다.

10.5 객체와 배열

const int STKS = 4;
Stock mystuff[STKS];                // 创建对象数组,自动调用默认构造函数
Stock sand[STKS] = {
    
                    // 创建对象数组,对不同的元素使用不同的构造函数
        Stock("sand1", 11, 11.1),   // 用括号括起,以逗号分割
        Stock(),                    // 使用默认构造函数
        Stock("sand3", 33)          // 只初始化了三个元素,剩下的自动调用默认构造函数
};
int i;
for (i=0; i<STKS; i++)
    sand[i].show();
const Stock *topsand = &sand[0];    // 定义一个类的const指针
for (i=1; i<STKS; i++)
    topsand = &(topsand->topval(sand[i]));
topsand->show();                    // 显示价格最高的一个对象

10.6 클래스 범위

  1. 클래스에 정의된 이름(예: 클래스 데이터 멤버 이름 및 클래스 멤버 함수 이름)의 범위는 전체 클래스이며 전체 클래스 범위의 이름은 클래스 내에서만 알려져 클래스 외부에서는 알려지지 않습니다. .
  2. 충돌을 일으키지 않고 다른 클래스에서 동일한 클래스 멤버 이름을 사용하십시오.
  3. 클래스의 멤버는 외부에서 직접 접근할 수 없으며, 퍼블릭 멤버 함수를 호출하려면 객체를 전달해야 합니다.
  4. 멤버 함수를 정의할 때 범위 결정 연산자(::)를 사용해야 합니다.
  5. 클래스 선언이나 멤버 함수 정의에서 장식되지 않은 멤버 이름을 사용할 수 있습니다.

클래스 범위 상수

클래스 선언은 객체의 형태만 설명할 뿐 객체를 생성하지는 않습니다. 따라서 개체가 생성되기 전까지는 값을 저장할 공간이 없습니다. const 변수 임에도 불구하고.
첫 번째 방법은 클래스에서 열거형을 선언하는 것입니다.enum {Month = 12}; // 기호 상수를 만들기 위해 Months는 기호 이름일 뿐이며 열거형 이름이 필요하지 않습니다. 범위는 전체 클래스입니다.
두 번째 방법은 클래스에서 static 키워드를 사용하는 것입니다. static const int Month = 12; // 전체 클래스로 범위가 지정된 정적 전역 변수.

10.7 추상 데이터 유형(추상 데이터 유형, ADT)

ADT는 언어나 구현 세부 정보를 도입하지 않고 일반적인 방식으로 데이터 유형을 설명합니다.

예를 들어 스택을 사용하면 항상 힙의 맨 위에서 데이터를 추가하거나 제거하는 방식으로 데이터를 저장할 수 있습니다.
예를 들어 C++ 프로그램은 스택을 사용하여 자동 변수를 관리합니다. 새로운 자동 변수가 생성되면 힙의 맨 위에 추가되고 죽으면 스택에서 제거됩니다.

루틴: 클래스의 멤버 함수를 사용하여 스택의 작동을 실현합니다. . . . . . 프로그래밍 실습 5

10.8 요약

  1. 객체 지향 프로그래밍은 프로그램이 데이터를 표현하는 방법을 강조합니다. OOP 방법을 사용하여 프로그래밍 문제를 해결하는 첫 번째 단계는 프로그램과의 인터페이스 측면에서 데이터를 설명하여 데이터 사용 방법을 지정하는 것입니다. 그런 다음 인터페이스를 구현하는 클래스를 설계합니다. 일반적으로 비공개 데이터 멤버는 정보를 저장하고 공개 멤버 함수(메서드라고도 함)는 데이터에 액세스하는 유일한 수단을 제공합니다. 클래스는 데이터와 메서드를 단일 단위로 결합하며 개인 정보 보호를 통해 데이터를 숨길 수 있습니다.
  2. 일반적으로 클래스 선언은 일반적으로 별도의 파일에 보관되는 두 부분으로 나뉩니다. 클래스 선언(함수 프로토타입으로 표현되는 메서드 포함)은 헤더 파일에 배치해야 합니다. 멤버 함수를 정의하는 소스 코드는 메서드 파일에 배치됩니다. 이는 구현 세부 사항에서 인터페이스 설명을 분리합니다. 이론적으로 클래스를 사용하려면 공용 인터페이스만 알면 됩니다. 물론 구현을 볼 수는 있지만(컴파일된 형식만 제공되지 않는 한) 프로그램은 값이 int로 저장된다는 것을 아는 것과 같은 구현 세부 사항에 의존해서는 안 됩니다. 프로그램과 클래스가 인터페이스를 정의하는 메서드를 통해서만 통신하는 한 프로그래머는 의도하지 않은 부작용에 대한 걱정 없이 자유롭게 모든 부분을 독립적으로 개선할 수 있습니다.
  3. 클래스는 사용자 정의 유형이고 개체는 클래스의 인스턴스입니다. 이것은 객체가 클래스에 의해 설명된 대로 new에 의해 할당된 메모리와 같은 이 유형의 변수임을 의미합니다. C++은 사용자 정의 유형을 가능한 한 표준 유형과 유사하게 만들려고 시도하므로 개체, 개체에 대한 포인터 및 개체 배열을 선언할 수 있습니다. 개체를 값으로 전달하고, 개체를 함수로 반환하고, 한 개체를 동일한 유형의 다른 개체에 할당할 수 있습니다. 생성자를 제공하면 개체 생성 시 개체를 초기화할 수 있습니다. 소멸자 메서드가 제공되면 프로그램은 개체가 죽은 후 해당 함수를 실행합니다.
  4. 각 개체는 클래스 메서드를 공유하면서 자체 데이터를 저장합니다. mr_object가 개체 이름이고 try_me( )가 멤버 함수인 경우 멤버 연산자 마침표를 사용하여 멤버 함수 mr_object.try_me( )를 호출할 수 있습니다. OOP에서 이 함수 호출은 mr_object 개체로 전송되는 try_me 메시지라고 합니다. try_me( ) 메서드에서 클래스 데이터 멤버를 참조할 때 mr_object 개체의 해당 데이터 멤버가 사용됩니다. 마찬가지로 함수 호출 i_object.try_me( )는 i_object 개체의 데이터 멤버에 액세스합니다.
  5. 멤버 함수가 여러 개체에서 작동하도록 하려면 추가 개체를 매개 변수로 전달할 수 있습니다. 메서드가 호출된 개체에 대한 명시적 참조가 필요한 경우 this 포인터를 사용할 수 있습니다. this 포인터는 호출 개체의 주소로 설정되므로 *this는 해당 개체의 별칭입니다.
  6. 클래스는 ADT를 설명하는 데 적합합니다. 공용 멤버 함수 인터페이스는 ADT에서 설명하는 서비스를 제공하고 클래스의 전용 부분과 클래스 메서드에 대한 코드는 클래스의 클라이언트에서 숨겨진 구현을 제공합니다.

10.9 검토 질문

  1. 수업이란 무엇입니까?
    클래스는 사용자 정의 유형의 정의입니다. 클래스 선언은 데이터가 저장되는 방법과 해당 데이터에 액세스하고 조작하는 데 사용되는 메서드(클래스 멤버 함수)를 지정합니다.
  2. 클래스는 추상화, 캡슐화 및 데이터 숨기기를 어떻게 구현합니까?
    클래스는 사람들이 클래스 메서드의 공용 인터페이스(추상화)를 통해 클래스 개체에 대해 수행할 수 있는 작업을 나타냅니다.
    클래스의 데이터 멤버는 비공개(기본값)일 수 있습니다. 즉, 데이터 숨기기인 멤버 함수를 통해서만 데이터에 액세스할 수 있습니다.
    구현의 특정 세부 사항(예: 데이터 표현 및 메서드 코드)은 숨겨져 있으며, 이는 캡슐화입니다.
  3. 객체와 클래스 사이의 관계는 무엇입니까?
    클래스는 사용 방법을 포함하여 유형을 정의합니다.
    객체는 클래스 정의에 따라 생성되고 사용되는 변수 또는 기타 데이터 객체(new에 의해 생성됨)입니다.
    클래스와 개체 간의 관계는 표준 유형과 해당 변수 간의 관계와 동일합니다.
  4. 함수라는 것 외에 클래스 함수 멤버와 클래스 데이터 멤버의 차이점은 무엇입니까?
    주어진 클래스의 여러 객체를 생성하는 경우 각 객체는 데이터에 대한 고유한 메모리 공간을 갖지만
    모든 객체는 동일한 멤버 함수 집합을 사용합니다(일반적으로 메서드는 공용이고 데이터는 전용이지만 이것은 단지 전략의 문제입니다. 수업에 대한 요구 사항이 아님).
  5. 클래스 생성자는 언제 호출됩니까? 클래스 소멸자는 어떻습니까?
    클래스의 생성자는 클래스의 객체가 생성되거나 생성자가 명시적으로 호출될 때 호출됩니다. 개체가 만료되면 클래스의 소멸자가 호출됩니다.
  6. 기본 생성자란 무엇이며 기본 생성자를 사용하면 어떤 이점이 있습니까?
    기본 생성자는 매개변수가 없거나 모든 매개변수에 기본값이 있는 생성자입니다.
    기본 생성자를 사용하면 초기화 생성자가 정의된 경우에도 객체를 초기화하지 않고 선언할 수 있습니다. 또한 배열 선언을 활성화합니다.
  7. 이것과 *이것은 무엇입니까?
    this 포인터는 클래스 메서드에서 사용할 수 있는 포인터로 메서드를 호출하는 데 사용되는 개체를 가리킵니다. 따라서 이것은 객체의 주소이고 *this는 객체 자체입니다.

10.10 프로그래밍 실습

5. 스택에서 고객 구조를 추가하고 제거하는 프로그램을 작성하십시오(스택은 Stack 클래스 선언으로 표시됨). 고객 구조가 삭제될 때마다 결제 금액이 합계에 추가되어 합계가 보고됩니다. 참고: Stack 클래스를 수정하지 않고 사용할 수 있어야 합니다. Item 이 unsigned long 대신 customer 유형이 되도록 typedef 선언만 수정하면 됩니다.

(아래 프로그램은 여덟 번째 질문의 의미를 담고 있습니다)
p5.h

#ifndef PRIMERPLUS_P5_H
#define PRIMERPLUS_P5_H
#include <iostream>
using namespace std;
struct customer
{
    
    
    char fullname[35];
    double payment;
};
typedef customer Item;         // 起别名,为存放不同的数据类型
void visit_item(Item &item);
class Stack
{
    
    
private:                // 私有部分放成员变量
    enum {
    
    MAX = 10};    // 枚举类型的符号常量
    Item items[MAX];    // holds stack items
    int top;            // 顶部堆栈项的索引,栈顶指针
public:
    Stack();                // 默认构造函数
    bool isempty() const;   // 判断是否为空
    bool isfull() const;    // 判断是否满了
    // push() returns false if stack already is full, true otherwise
    bool push(const Item & item);   // 入栈
    // pop() returns false if stack already is empty, true otherwise
    bool pop(Item & item);          // 出栈

    void visit(void (*pf)(Item &)); // 访问数据项以及执行操作
    // pf指向一个将Item引用作为参数的函数(不是成员函数)
    // visit( )函数将该函数用于列表中的每个数据项。
};
#endif //PRIMERPLUS_P5_H

p5.cpp

#include "p5.h"

Stack::Stack() // create an empty stack
{
    
    
    top = 0;            // 初始化栈顶指针
}

bool Stack::isempty() const
{
    
    
    return top == 0;    // 是否等于最底层
}

bool Stack::isfull() const
{
    
    
    return top == MAX;  // 是否等于最高层
}

bool Stack::push(const Item & item)
{
    
    
    if (top < MAX)      // 入栈条件
    {
    
    
        items[top++] = item;
        return true;
    }
    else
        return false;
}

bool Stack::pop(Item & item)
{
    
    
    if (top > 0)
    {
    
    
        item = items[--top];
        return true;
    }
    else
        return false;
}

void Stack::visit(void (*pf)(Item &))
{
    
    
    for (int i=0; i<top; i++)
        pf(items[i]);
}

void visit_item(Item &item)
{
    
    
    cout << "fullname:" << item.fullname << endl;
    cout << "payment:" << item.payment << endl;
}

usep5.cpp

#include <iostream>
#include <cctype>   // or ctype.h
#include "p5.h"
int main()
{
    
    
    using namespace std;
    Stack st;       // create an empty stack
    char ch;
    customer cust;
    double sum = 0.0;
    cout << "Please enter A/a to add a purchase order, "
         << "P/p to process a PO, or Q/q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
    
    
        while (cin.get() != '\n')   // 消耗回车
            continue;
        if (!isalpha(ch))
        {
    
    
            cout << '\a';
            continue;
        }
        switch(ch)
        {
    
    
            case 'A':
            case 'a':   cout << "Enter a customer's fullname you want to push to stack (string):";
                        cin.getline(cust.fullname, 35);
                        cout << "Enter a customer's payment (double):";
                        cin >> cust.payment;
                        if (st.isfull())
                            cout << "stack already full\n";
                        else
                        {
    
    
                            st.push(cust);
                            st.visit(visit_item);   // 显示
                        }
                        break;
            case 'P':
            case 'p':   if (st.isempty())
                            cout << "stack already empty\n";
                        else
                        {
    
    
                            st.pop(cust);
                            sum += cust.payment;
                            cout << cust.fullname << " is popped\n";
                            cout << cust.payment << " is popped\n";
                            cout << "sum panyment :" << sum << endl;
                        }
                        break;
        }
        cout << "Please enter A/a to add a purchase order, "
             << "P/p to process a PO, or Q/q to quit.\n";
    }
    cout << "Bye\n";
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_39751352/article/details/126808697
Recomendado
Clasificación