Chapter 10 - Objects and Classes

Object-Oriented Programming (OOP) is a specific, conceptual approach to designing programs, and the following are the most important OOP features:

  • abstract
  • Encapsulation and data hiding
  • polymorphism
  • inherit
  • code reusability

To implement these features and combine them, the most important thing C++ does is provide the class

With procedural programming, first consider the steps to follow and then how to represent the data

When using the OOP method, first consider the object from the perspective of the user - describe the data required by the object and describe the operations required for the user to interact with the data. After completing the description of the interface, it is necessary to determine how to implement the interface and data storage

abstraction and class

Life is full of complexity, and one of the ways to deal with complexity is simplification and abstraction

what is the type 

Specifying primitive types accomplishes three things:

  • Determine the amount of memory required by the data object
  • Decide how to interpret the bits in memory (long and float occupy the same number of bits in memory, but convert them to numbers differently)
  • Decide what operations or methods can be performed with the data object 

classes in C++

 A class is a C++ tool for converting abstractions into user-defined types, combining data representation and methods for manipulating data into a neat package.

Let's look at a class that represents stocks

Taking a certain stock currently held by a person as a basic unit, the data representation includes the number of stocks he holds, and the operations that he can perform are limited to

  • get stock
  • increase holdings
  • sell stock
  • update stock price
  • Explicit information about stock holdings

The public interface of the stock class can be defined according to the above list. In order to support this interface, some information needs to be stored

  • stock company name
  • Number of shares held
  • US stock prices
  • total stock value 

Next define the class, half of the class consists of two parts

  • Class declaration: describe the data part in the form of data members, and describe the common interface in the form of member functions (methods)
  • Class method definition: describes how to implement class member functions 

 Usually put the class definition in the header file and the implementation (the code for the class methods) in the source code file

stock00.h

#ifndef STOCK00_H_
#define STOCK00_H_

#include<string>
class Stock
{
private:
	std::string company;
	long shares;
	double share_val;
	long total_val;
	void set_tot() { total_val = shares * share_val; }
public:
	void acquire(const std::string& co, long n, double pr);
	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show();
};
#endif

 The C++ keyword class indicates that these codes define a class design, and this syntax indicates that Stock is the type name of this new class. This declaration allows us to declare variables of type Stock - called objects or instances, each object represents a stock, for example, two Stock objects are created below

Stock sally;
Stock solly;

1. Access Control

We can see the keywords private and public, which describe access control to class members. Programs that use class objects can directly access the common parts, but can only access the private members of the object through public member functions (or friend functions). For example, to modify the shares member of the Stock class, only through the member function of Stock. Therefore, the public member function is a bridge between the program and the private members of the object, providing an interface between the object and the program, and preventing the program from directly accessing the data is called data hiding.

Class design separates the common interface from the implementation details as much as possible. The common interface represents the abstract components of the design. Putting the implementation details together and separating them from the abstraction is called encapsulation. Data hiding (putting data in a private part of a class) is also a form of encapsulation.

 Data hiding not only prevents direct access to the data, but also relieves the developer (user of the class) of knowing how the data is represented

2. Control access to members: public or private

Whether a class member is a data member or a member function, it can be declared in the public or private part of the class. But since hiding data is one of the main goals of OOP, data items are usually placed in the private section, and the member functions that make up the class interface are placed in the public section

Class and Struct The only difference between them is that the default access access type of Struct is public while class is private

Implement class member functions

You also need to create the second part of the class description: providing code for the member functions represented by the prototypes in the class declaration.

Member function definitions are similar to regular function definitions in that they have a function header and a function body, and can also have a return type and parameters. But they also have two special features:

  • When defining a member function, use the scope resolution operator (::) to identify the class to which the function belongs
  • Class methods can access the private components of the class

stock00.cpp 

#include<iostream>
#include"stock00.h"

void Stock::acquire(const std::string& co, long n, double pr)
{
	company = co;
	if (n < 0)
	{
		std::cout << "Number of shares can't be negative; "
			<< company << " shares set to 0.";
		shares = 0;
	}
	else
		shares = n;
	share_val = pr;
	set_tot();
}
void Stock::buy(long num, double price)
{
	if (num < 0)
	{
		std::cout << "Number of shares purchased can't be negative. "
			<< "Transaction is aborted.\n";
	}
	else
	{
		shares += num;
		share_val = price;
		set_tot();
	}
}
void Stock::sell(long num, double price)
{
	using std::cout;
	if (num < 0)
	{
		cout << "Number of shares can't be negative. "
			<< "Transsaction is aborted.\n";
	}
	else if (num > shares)
	{
		cout << "You can't sell more than you have! "
			<< "Transaction is aborted.\n";
	}
	else
	{
		shares -= num;
		share_val = price;
		set_tot();
	}
}
void Stock::update(double price)
{
	share_val = price;
	set_tot();
}
void Stock::show()
{
	std::cout << "Company: " << company << '\n';
	std::cout << "Shares: " << shares << '\n';
	std::cout << "Shares Price: " << share_val << '\n';
	std::cout << "Total Worth: " << total_val << '\n';
}

1. Inline method

Functions located in the class declaration will automatically become inline functions. You can also define member functions outside the class declaration and make them inline functions. To do this, you only need to use the inline qualifier when defining the function in the class implementation part:

class Stock
{
private:
	...
		void set_tot();			//definition kept separate
public:
	...
};
inline void Stock::set_tot()	//use inline in definition
{
	total_val = shares * share_val;
}

 2. Which object the method uses

Each new object created has its own storage space for storing internal variables and class members; but all objects of the same class share the same set of methods, that is, there is only one copy of each method 

 use class

Now that you know how to define a class and its methods, let's create a program that creates and uses class objects. The goal of C++ is to make using classes the same as using the basic built-in types (int, double, etc.)

usestock00.cpp

#include<iostream>
#include"stock00.h"
int main()
{
	Stock stock1;
	stock1.acquire("NanoSmart", 20, 12.5);
	stock1.show();
	stock1.buy(15, 25.5);
	stock1.show();
	stock1.sell(400, 18.8);
	stock1.show();
	stock1.buy(1000000, 30.25);
	stock1.show();
	stock1.sell(500000, 10.55);
	stock1.show();
	return 0;
}

 

The main() function is only used to test the design of the Stock class. After the Stock class behaves as expected, you can use the Stock class as a user-defined type in other programs.

 summary

The first step in specifying a class design is to provide a class declaration. A class declaration is similar to a structure declaration and can include data members and function members. A declaration has a private section, in which the declared members are accessible only through member functions; a declaration also has a public section, in which the declared member functions are directly accessible by programs using the class object. Usually data members are placed in the private section, and member functions are placed in the public section, so the format of a typical class declaration is as follows:

class className
{
    private:
        data member declarations
    public:
        member function prototypes
};

The content of the common part constitutes the abstract part of the design - the common interface. Encapsulating data into private parts protects the integrity of the data, which is known as data hiding. C++ makes it easy to implement OOP features such as abstraction, data hiding, and encapsulation through classes

The second step in specifying the class design is to implement the class member functions. The full function definition can be provided in the class declaration instead of the function prototype, but it is common practice to provide the function definition by itself (unless the function is small). In this case, the scope resolution operator needs to be used to indicate which class the member function belongs to.

class constructor and destructor

 One of the goals of C++ is to make using class objects the same as using standard types, but so far, the code provided above does not allow us to initialize Stock objects like ints

int year = 2001;
struct thing 
{
    char *pb;
    int m;
};
thing anb ={"wodget",-23};    //valid initialization
Stock hot ={"Sussy",200,50.5; //error

The reason why the Stock object cannot be initialized as above is that the access state of the data part is private, which means that the program cannot directly access the data members. Programs can only access data members through member functions, so appropriate member functions need to be designed to initialize objects.

C++ provides a special member function - class constructor, which is specially used to construct new objects and assign values ​​to their data members

Declare and define constructor

Now you need to create the Stock constructor. Since you need to provide 3 values ​​for the Stock object, you should provide 3 parameters to the constructor. (The fourth value total_val member is calculated based on shares and share_val, so it is not necessary to provide this value for the constructor), the following is a possible definition of the constructor

Stock::Stock(const string & co, long n ,double pr)
{
    company = co;
    if(n < 0)
    {
        std::cout<<"Number of shares can't be negative; "
                << company <<" shares set to 0";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
} 

Those who are not familiar with constructors may write the following statement

Stock::Stock(const string & company, long shares ,double share_val)
{
...
}

This is wrong. The parameters of the constructor represent not the class members, but the values ​​assigned to the class members, so the parameter names cannot be the same as the class members.

use constructor

C++ provides two ways to initialize objects using constructors. The first way is to call the constructor explicitly:

Stock food = Stock("World Cabbage",250,1.25);

This sets the company member function of the food object to the string "World Cabbage", the shares member to 250, and so on

Another way is to call the constructor implicitly:

Stock garment("Furry Mason",50 ,2.5);

default constructor

The default constructor is the constructor used to create objects when no explicit initial value is provided, which is the following situation

Stock stock1; 

The reason this statement works is that C++ will automatically provide a default constructor if no constructor is provided.

The compiler provides a default constructor only if no constructor is provided in the program.

There are two ways to define a default constructor . One is to provide default values ​​for all parameters of existing constructors

Stock(const string & co ="Error" ,int n = 0, double pr = 0.0);

 Another way is to define another constructor through function overloading - a constructor with no parameters

Stock();

Since there can only be one default constructor, don't do both. 

(Classes should generally be designed with a default constructor that implicitly initializes all class members.)

Once a default constructor has been created using any of the above methods (either with no parameters or with default values ​​for all parameters), object variables can be declared without explicit initialization of them.

destructor

 After an object is created with a constructor, it is the program's responsibility to keep track of the object until it expires. When the object expires, the program will automatically call a special member function - the destructor. The destructor does the cleanup

For example, if the constructor uses new to allocate memory, the destructor will use delete to free that memory. Stock's constructor does not use new, so the destructor actually has nothing to do, just let the compiler generate an implicit destructor that does nothing

The name of the destructor : prefix the class name with ~. So the destructor of the Stock() class is ~Sock().

Unlike the constructor, the destructor has no parameters , so the prototype of the Stock destructor must be like this: ~Stock();

Since the destructor will be called automatically when the class object expires, there must be a destructor. If the programmer does not provide a destructor, the compiler will implicitly declare a default destructor.

 const member function

Please see the code snippet below

const Stock land = Stock("Kuudorn");
land.show();

For current C++, the compiler will refuse to execute the second line of code. Because the code of show() cannot ensure that the calling object is not modified-the calling object, like const, should not be modified. We used to solve this kind of problem by declaring function parameters as const references or pointers to const, but there is a syntax problem here: the show() function does not have any parameters. A new syntax is needed - guaranteeing that the function does not modify the calling object. The C++ solution is to place the const keyword after the function's parentheses .

void show() const;

 A class function declared and defined in this way is called a const member function, as long as the class method does not modify the calling object, it should be declared as const

Summary of constructors and destructors

A constructor is a special class member function that is called when an object of the class is created. The name of the constructor is the same as the name of the class, but through function overloading, multiple constructors with the same name can be created, provided that the signature (argument list) of each function is different. Also the constructor does not declare a type. . Typically, constructors are used to initialize members of class objects, and the initialization should match the parameter list of the constructor

The default constructor has no parameters, so if the object is created without explicit initialization, the default constructor will be called. If no constructor is provided in the program, the compiler defines a default constructor for the program; otherwise, you must provide the default constructor yourself.

The default constructor can have no parameters; if it does, it must provide default values ​​for all parameters

Just like a program will call a constructor when an object is created, a program will call a destructor when an object is deleted, and there can only be one destructor per class. The destructor has no return value and no parameters, and its name is the class name preceded by ~.

this pointer

 So far, each class member function has involved only one object, the object that called it, but sometimes a method may involve two objects, in which case C++'s this pointer needs to be used

Suppose you want to compare the Stock objects stock1 and stock2, and assign the one with the higher total stock price to the top object, you can use one of the following two statements:

top = stock1.topval(stock2);
top = stock2.topval(stock1);

The first format implicitly accesses stock1 and explicitly accesses stock2; the second format explicitly accesses stock1 and implicitly accesses stock2; no matter which method is used, the two objects will be compared and the object with the higher total value of the stock price will be returned. This approach is actually a bit confusing, and it would be clearer if we could use the relational operator > to compare the two objects.

const Stock & Stock::topval(const Stock & s)const
{
    if(s.total_val > total_val)
        return s;
    else
        return ???
}

 s.total_val is the total value of the object passed as a parameter, and total_val is the total value of the object used to call the method. If s.total_val is greater than total_val, the function will return a reference to s; otherwise it will return the object used to call the method, the question is how to call this object ?

The way C++ solves this problem is to use a special pointer called this. The this pointer points to the object used to call the member function .

This function calls stock1.topval(stock2) to set this to the address of the stock1 object

 

 All class methods set the this pointer to the address of the object that called it.

Now we can use this pointer to complete the above method definition

const Stock & Stock::topval(const Stock & s)const
{
    if(s.total_val > total_val)
        return s;
    else
        return *this;
}

 A return type of reference means that the calling object itself is returned, not a copy of it.

class scope

Earlier we introduced global scope and local scope. A global variable can be used anywhere in the file it belongs to, while a local variable can only be used in the code block it belongs to

C++ introduces a new kind of scope: class scope

The scope of the names defined in the class (such as class data member names and class member function names) is the entire class, and the names with the scope of the entire class are known only in the class and are not known outside the class. So the same class member name can be used in different classes without causing conflicts

 

Guess you like

Origin blog.csdn.net/yangSHU21/article/details/131659264