C++ Classes and Objects
class
Basic class syntax
class ClassName {
public.
// public member
Type memberVariable; // Data member
ReturnType memberFunction(); // member function declaration
private.
// private member
Type privateMemberVariable; // Data member.
ReturnType privateMemberFunction(); // Member function declaration
protected.
// Protected member
Type protectedMemberVariable; // data member
ReturnType protectedMemberFunction(); // Member function declaration
}
Important points
-
public
:public access, accessible from outside the class -
private
: Private access, accessible only from within the class. -
protected
: Protected access, accessible only to internal and derived classes of the class. -
Note that the default permissions are
private
This is where you can implement manipulated permissions for member variables
In class, when declaring the size of an array, if the variable used to declare the size is a variable within the class, it should be as follows
class Map{
public:
const static int maxn =4343;
int next[maxn]={};
};
Alternatively, it is recommended to use thearray
orvector
A warning to posterity about the static modifier in class
Access to functions and variables in a class without the static modifier requires a created object.
But with the static modifier, it's a different story. Variables and functions modified with static belong only to the class itself, not to a specific object, but they still have access rights set!
It can be accessed directly by
class classname{ public: static functionname(){/*content*/} static cnt; }; int main (){ classname::functionname(); cout<<classname::cnt<<endl; }
Constructors and destructors
Constructor syntax:Class name (){}
- Constructor with no return value and without void
- The function name is the same as the class name
- Constructors can have parameters, so overloading can occur
- The program will automatically call the constructor when calling the object, no need to call it manually, and it will only be called once.
Destructor syntax: ~class name(){}
- destructor with no return value and no void
- The function name is the same as the class name, preceded by the symbol ~.
- A destructor cannot have arguments, so overloading cannot occur
- The program will automatically call destruct before the object is destroyed, no need to call manually, and will only be called once.
Notes: These two functions will be set with access rights that can restrict the creation and destruction of the class!
Constructor Classification and Invocation
Two ways to categorize:
Parameters: Parametric and non-parametric constructs
Classification by type: normal and copy constructs
Three types of calls:
Parenthetical method ``````
display method
implicit conversion method
Difference between deep copy and shallow copy:
Shallow Copy
A shallow copy is a copy of an object where the new object shares the same memory address as the original object. In other words, a shallow copy copies only the value of the object (i.e., the value of pointers and basic data types), not the actual object pointed to by the pointer.
Features:
- shared reference: If the object contains pointers or references, a shallow copy copies only the values of the pointers, so the pointers to the original and copied objects point to the same area of memory.
- performances: Shallow copies are usually faster than deep copies because they only need to copy the values of pointers and basic data types, rather than recursively copying all referenced objects.
- potential problem: Since the original and copy objects share the same memory area, when one object modifies what the pointer points to, the contents of the other object are also affected. This sharing can lead to dangling pointers (if one object frees the shared memory, the other object becomes dangling) or data inconsistency problems.
Deep Copy
Deep copy, on the other hand, creates a new object and recursively copies all the objects referenced by the original object. In other words, a deep copy copies not only the value of the object, but also all the objects pointed to by pointers contained within the object, creating a completely separate new object.
Features:
- independent: There is no shared memory area between the original object and the deep copy object; modifying one object does not affect the other.
- performances: Deep copy can be slow because it involves recursively copying everything the pointer points to.
- memory management: Deep copies usually require additional code to be written to ensure proper memory management, such as freeing allocated memory in destructors to avoid memory leaks.
Constructor initialization list
class Person {
public:
////Traditional way initialization
//Person(int a, int b, int c) {
// m_A = a;
// m_B = b;
// m_C = c;
//}
//Initialization List Method Initialization
Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
void PrintPerson() {
cout << "mA:" << m_A << endl;
cout << "mB:" << m_B << endl;
cout << "mC:" << m_C << endl;
}
private:
int m_A;
int m_B;
int m_C;
};
Order of construction and destructor calls for class objects as class members
A member of a C++ class can be an object of another class; we call that member an object member.
//The order of construction is: first call the construction of the object member, then call the construction of this class.
//Deconstruction is in the reverse order of construction
static member
static member variable
-
All objects share the same data
-
Allocating memory at the compilation stage
-
In-class declaration, out-of-class initialization (must remember to initialize or compilation errors don't show up!!!)
class Example { public. static int staticVar; // Declare static member variables. }; // declare static member variable int Example::staticVar = 0; // Define and initialize static member variables }; int Example::staticVar = 0; // define and initialize static member variables
Static member functions (static)
- All objects share the same function
- Static member functions can only access static member variables (in fact, you only need to add an object to the argument to access the object's member variables)
Static functions can be declared inside the class and defined outside the class!
Constant member (const)
const modifies a member function
-
is guaranteed not to change the state of the object, but can be called to change static-modified variables
-
Only other const member functions can be called.
To ensure that no changes are made to the object in the const member function
You can call static functions!!!
-
can return a value, but if it returns a pointer OR a reference, it must be accompanied by const
class Type{ int a; const int* GO() const{//int const* GO() const That's fine. return &a; } };
This does not apply to static variables: When the returned value is the address or reference of a static variable, it is possible to not use the static
Note: Here again in the distinction between
const int *
int const *
int * const
The first two are the same, the defined pointer cannot modify the corresponding address
The third means that the address pointed to by the pointer cannot be modified, but the contents can be modified by the pointer
-
member variable plus\(mutable\) can be modified by the const function and return non-constant pointers.
Summary. Constant functions ensure that no modifications of any kind, including pointers and references, are made to the object through them, but correspond to the\(static,mutabl\) Exceptions for Modified Variables
The way the const keyword is used:
const int* () const{}
previous one\(const\) is used to qualify the return value, the latter is used to qualify the function as a static function
Note: Functions can be inverted back\(const\) value, but it doesn't make sense because the value it returns is a copy. But returning the\(const\) of pointers and references are meaningful.
const MyClass& getObject() const {
static MyClass obj;
return obj;
}
OR
const MyClass* getObject() const {
static MyClass obj;
return obj;
}
normal object
- Declaring an object with const in front calls it a constant object.
- Constant objects can only call constant functions
- A constant object can modify static variables, call static functions, modify the\(mutable\) variant
friendly dollar
The private and protected variables and functions of a class in C++ can be made accessible to other classes and functions by declaring friend meta
Youyuan class (of birds)
palindrome function (math.)
Youyuan global function
class People{
friend class Dorm; //Youyuan class (of birds)
friend void get_password(const People&,const Dorm&); //palindrome function (math.)
public:
const int ID;
void Change_drompassword(const int&,const int&,const int &,Dorm&);
public:
People(int id,int Password): ID(id),password(Password){
cout<<"creat a new person\n";
}
private:
int password;
};
class Dorm{
friend class People;
friend void get_password(const People & a,const Dorm & b);
friend void People::Change_drompassword(const int&,const int&,const int &,Dorm&); //Youyuan class (of birds)函数
private:
int get_number(){
return dorm_number;
}
int dorm_number;
int dorm_password;
public:
Dorm(int number,int password):dorm_number(number),dorm_password(password){
cout<<"creat a drom!\n";
}
};
Attention:
- Before declaring a friend class function, make sure that the class has been declared (declaring it early is not acceptable, because the complier only knows that the class exists but not what it is). For example, swapping the definitions of the two classes in the above code results in the $ incompleting $ error
- When declaring a function with friends, the parameter list doesn't have to be written with the parameter names, but the\(const~,\&~\) unpluggable
Notes on Tomodachi
- Not having a relationship of succession: Friendships are not inherited. For example, if class B is a friend of class A, and class C inherits class A, class C will not automatically become a friend of class B.
- Friends are not transitive: If class A is a friend of class B, class B is not automatically a friend of class A.
- Friendly meta-relationships are unidirectional: Even though class A is a friend of class B, class B does not automatically have access to the private members of class A unless class B also explicitly declares class A as a friend.
\(C++\) The input and output streams in the
input flow
-
Used to read data from an external source such as a keyboard or file.
-
The main input stream objects are
std::cin
, which represents the standard input stream. -
()
Used to check if the input was successful -
()
function (math.)-
Ignore the specified number of characters
std::(count);//count stands for quantity
-
Ignored until a specific character or EOF:
std::(count, delimiter);//delimiter stands for the delimiter, which is also rounded off std::(numeric_limits<std::streamsize>::max(), '\n')://Ignore specific characters in the input stream or until a newline is encountered.
-
output stream
- Used to output data to an external target (such as a monitor or file).
- The main output stream objects are
std::cout
, which represents the standard output stream. -
()
Flushes the output stream to ensure that all buffers are output to the terminal.
error stream
-
Used to output error messages.
-
The main error stream objects are
std::cerr
It is used to print error messages.
Log Stream
- Used to output warning or log messages.
- The main log stream objects are
std::clog
。
Notes: All four of the above standard streams are used in the same way
int x; std::cin >> x; std::cout << "Hello, World!" << std::endl; std::cerr << "An error occurred!" << std::endl; std::clog << "This is a log message." << std::endl;
file stream
In addition to standard streams, C++ also supports file streams, which are used to read data from a file or write data to a file. The main classes of file streams are:
-
std::ifstream
: Used for input file streams. -
std::ofstream
: Used to output a file stream. -
std::fstream
: Used for reading and writing file streams (i.e., supports both input and output).
#include <iostream>
#include <fstream> // Introducing Document Streaming
int main() {
// write to a file
std::ofstream outFile("");
if (outFile.is_open()) {
outFile << "Hello, File!" << std::endl;
();
}
// Read file
std::ifstream inFile("");
std::string line;
if (inFile.is_open()) {
while (getline(inFile, line)) {
std::cout << line << std::endl;
}
();
}
return 0;
}
File Open Mode
First of all, you can set the open mode in the open file stream, the setting method is as follows
std::fstream file("", std::ios::in | std::ios::out | std::ios::ate);
Notes: When multiple open modes are to be used, you can use the|
Link it
The next step is to introduce the file opening mode
-
std::ios::in
: Used for read operations. -
std::ios::out
: For write operations, the contents of the file are overwritten (if the file exists). -
std::ios::app
: Used for append operations, all data written will be appended to the end of the file. -
std::ios::ate
: Position the file pointer to the end of the file when the file is opened, for situations where reading or writing needs to start at the end of the file. -
std::ios::trunc
: If the file already exists, the contents of the file will be cleared, usually in the same way as thestd::ios::out
Used together. -
std::ios::binary
Opens files as binary without any text conversion (e.g. line break conversion).#include <iostream>; #include <fstream>; #include <vector>. int main() { // filename const char* filename = ""; // Open the file in binary mode std::ifstream file(filename, std::ios::binary); // Check if the file was opened successfully if (!file) { std::cerr << "Could not open file: " << filename << std::endl; return 1; } // Move the file pointer to the end of the file to get the file size. (0, std::ios::end); std::streamsize size; } std::streamsize size = (); (0, std::ios::beg); // Use std::vector to store the contents of a file. std::vector<char> buffer(size); // Read the contents of the file into the buffer if (((), size)) { // Process the contents of the file (in the example, only the size of the file is output) std::cout << "File size: " << size << " bytes" << std::endl; // Process the contents of the buffer as needed. // Here you can process the data in the buffer as needed. } else { std::cerr << "Failed to read file" << std::endl; } // Close the file (); return 0; } } /* Filename: You need to specify the name of the file you want to read. The example uses "". Open file: std::ifstream file(filename, std::ios::binary); This line of code opens the specified file and reads it in binary mode. Getting the size of a file: Use the seekg and tellg methods to determine the size of a file. Read the contents: ((), size); This line of code reads the contents of the file into a buffer. Error checking: After opening a file and reading it, check if these operations were performed successfully. Close the file: Use (); to close the file. */
The above opens in theifstream
ofstream
There is no problem to use it in the input file stream, just be careful not to open the output file on top of the input file stream.Issues that can cause file streams to not open properly
Let's conclude with a look at thefstream
Some of the problems that occur when using these file openers in the
-
To be used on its own
app,ate,trunc
Neither will work because they don't give the read/write mode of the file, so add thein
out
-
There's a way to play it.
fstream IN(".in", std::ios::out); cout<<IN.is_open()<<endl; int a; IN>>a; IN<<1111<<endl;
Then you'll realize the output is lonely.
Locating the output pointer
std::ios::beg //beginning pointer
std::ios::cur //current pointer
std::ios::end //end of file
collocation functionseekg()
utilization
// move the read pointer to the beginning of the file
(0, std::ios::beg);
// Move the read pointer 5 characters back from the current pointer position
(5, std::ios::cur); // Move the read pointer to the beginning of the file.
\(istream\) together with\(ostream\)
These are the two types of streams, the most basic, one is an input stream and the other is an output stream
It's these two that are used when overloading the operator
// The author swung for the fences, see you at BF5!
Operator Overloading
Two forms of overloaded functions
Overloaded functions have accessibility issues just like any other function!!!!
Friendly function overloading
class Grade{
friend ostream& operator<<(ostream& out,const Grade & P);
private:
int grade_Chnese,grade_program,grade_math;
Grade(int a,int b,int c,bool OP):grade_Chnese(a),grade_program(b),grade_math(c){
if(OP) cout<<"insert grade succesfully!\n";
}
Grade(){}
};
ostream& operator <<(ostream & OUTT,const Cnt & b){
OUTT<<<<endl;
return OUTT;
}
Notes: In friend function overloading, the two arguments represent the left and right operators (which can actually be left out)friend
If you don't need to access theprivate
cap (a poem)protected
(if any)
Member function overloading
class Grade{
public:
Grade operator + (const Grade& A) const{
return Grade(A.grade_Chnese+grade_Chnese,A.grade_program+grade_program,A.grade_math+grade_math,0);
}
}
Notes: In member function overloading the object itself will be used as the left operand and the arguments as the right operands
Example of operator overloading
Notes: + - * / > < >= <= ==
It's all relatively simple, the reference structure is the same as the weight structure
Notes: It is recommended to use constant variables + references when defining parameters to prevent accidental changes as well as to speed up the process
Left-right shift symbols (input/output stream operators)
Since in left-right shift notation, the object is always in the right operand, you can only use the method of the friend function
class Grade{
friend ostream& operator<<(ostream& out,const Grade & P);
friend class People;
private:
int grade_Chnese,grade_program,grade_math;
Grade(int a,int b,int c,bool OP):grade_Chnese(a),grade_program(b),grade_math(c){
if(OP) cout<<"insert grade succesfully!\n";
}
Grade(){}
public:
Grade operator + (const Grade& A) const{
return Grade(A.grade_Chnese+this->grade_Chnese,A.grade_program+this->grade_program,A.grade_math+this->grade_math,0);
}
};
ostream& operator<<(ostream& Gut,const Grade & P){
Gut<<P.grade_Chnese<<" "<<P.grade_program<<" "<<P.grade_math<<endl;
return Gut;
}
self-adding and self-decreasing symbol
class Cnt{
public.
Cnt(long double a=0){cnt=a;}
Cnt(long double a=0){cnt=a;}
Cnt& operator ++(){
(this->cnt)+=1; }
return *this;
}// Modify first, return reference later
Cnt operator ++(int){// This int is used as a placeholder, a flag used by the C++ compiler to distinguish between these two overloads, of no real significance!
Cnt a=*this;
cnt+=1.
Cnt a=*this; cnt+=1; return a.
}// Return the value first, modify it later.
};
predecessor
Basic concepts of inheritance:
- Base Class: Classes that are inherited and provide shared properties and methods.
- Derived Class: A class that inherits from a base class can reuse members of the base class and can extend or modify those members.
Types of inheritance
-
Public Inheritance (PI):
- The most common form of inheritance, indicating that the derived class "is a special type" of the base class.
- Public members of base classes remain public in derived classes, and protected members of base classes remain protected in derived classes.
-
Private members of base classes cannot be accessed directly.
class Base { public: int pubValue; protected: int protValue; private: int privValue;
};
class Derived : public Base {
public:
void accessMembers() {
pubValue = 1; // can access public members
protValue = 2; // Protected members can be accessed.
// privValue = 3; // No access to private members.
}
};
2. **Protected Inheritance**:
- Both public and protected members of the base class become protected members in the derived class.
- External code is not allowed to access these members through the derived class, but they are accessible within the derived class.
```cpp
class Derived : protected Base {
public.
void accessMembers() {
pubValue = 1; // access to public members (now protected)
protValue = 2; // Can access protected members
// privValue = 3; // Can't access private members.
}
}
```
3. **Private Inheritance**:
- Both public and protected members of the base class become private members in the derived class.
- External code cannot access these members through the derived class, but they are accessible within the derived class.
```cpp
class Derived : private Base {
public.
void accessMembers() {
pubValue = 1; // access to public members (now private)
protValue = 2; // Access to protected members.
// privValue = 3; // Can't access private members.
}
}
Attention: While it is true that the base classprivate
member cannot be called in the derived class, but it is actually inherited, just hidden by the compiler.
Characteristics of inheritance
-
Constructors and destructors:
- The constructor of a derived class calls the constructor of the base class. The constructor of the base class is executed first and the constructor of the derived class is executed later.
- The destructor of a derived class calls the destructor of the base class. The destructor of the derived class is executed first and the destructor of the base class is executed later.
class Base {
public:
Base() { std::cout << "Base Constructor\n"; }
virtual ~Base() { std::cout << "Base Destructor\n"; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived Constructor\n"; }
~Derived() { std::cout << "Derived Destructor\n"; }
};
If you want to enter parameters into the constructor of the base class,This can be done in the following ways:
```C++
class People{
public:
string Name;
People(){cout<<"creat a people\n";}
People(const string& name,const string& id,const string& phone_num):Name(name),ID(id),Phone_num(phone_num){cout<<"creat a new people\n";}
protected:
string ID;
string Phone_num;
};
class Student: public People{
public:
Student(){cout<<"creat a new student\n";}
Student(const string& name,const string& id,const string& phone_num):
People(name,id,phone_num){//It's here.,Injecting Parameters Using a Parameter List
cout<<"creat a new student\n";
}
};
It's not right to write that way:
class People{
public:
string Name;
People(){cout<<"creat a people\n";}
People(const string& name,const string& id,const string& phone_num):Name(name),ID(id),Phone_num(phone_num){cout<<"creat a new people\n";}
protected:
string ID;
string Phone_num;
};
class Student: public People{
public:
Student(){cout<<"creat a new student\n";}
Student(const string& name,const string& id,const string& phone_num):
People::Name(name),People::ID(id),People::Phone_num(phone_num){
cout<<"creat a new student\n";
}
};
-
Variable Name Conflicts.
In multiple inheritance, if the same name occurs in the parent class, the subclass should be scoped when it is used.
class People{ public: string Name; People(){cout<<"creat a people\n";} People(const string& name,const string& id,const string& phone_num):Name(name),ID(id),Phone_num(phone_num){cout<<"creat a new people\n";} void Print(); protected: string ID; string Phone_num; }; class Student: public People{ public: string ID; Student(){cout<<"creat a new student\n";} Student(const string& name,const string& id,const string& phone_num,const string & IDD):People(name,id,phone_num),ID(IDD){ cout<<"creat a new student\n"; } void OKK(){ cout<<People::ID<<" "<<ID<<endl;//here are,Adding a scope to theokparticle placed after each item in a list of examples } };
-
virtual inheritance:
- Used to solve the diamond inheritance (diamond inheritance) problem by ensuring that the base class is initialized only once.
- By prefixing the base class with
virtual
keyword to declare virtual inheritance.
class Base { public: int value; }; class Derived1 : virtual public Base {}; class Derived2 : virtual public Base {}; class Final : public Derived1, public Derived2 {};//existFinal There would only be one Base::value Avoids redundancy and dichotomy
-
multiple inheritance:
- C++ supports a class inheriting from multiple base classes. This approach allows a class to inherit from more than one class at a time, but care needs to be taken to avoid naming conflicts and inconsistencies.
class A { public: void funcA() {} }; class B { public: void funcB() {} }; class C : public A, public B { public: void funcC() {} };
Considerations for Using Inheritance
- Correctness of inheritance: Ensure that the use of inheritance expresses the actual relationship between classes and avoid using inheritance to simply reuse code.
- encapsulation: The use of protected or private inheritance reduces dependence on the implementation details of the base class.
- polymorphism: Runtime polymorphism is implemented using virtual functions and dynamic binding.
Inheritance is a powerful feature of C++, but using it wisely is important to maintain readability and maintainability of your code.
polymorphic
There are two main types of polymorphism in C++:
- Compile-time polymorphism (static polymorphism)
- Runtime polymorphism (dynamic polymorphism)
Compile-time polymorphism (static polymorphism)
Compile-time polymorphism occurs during the compilation phase and is mainly realized through Function Overloading and Operator Overloading.
Runtime polymorphism (dynamic polymorphism)
virtual function: declared in the base class asvirtual
member functions that allow the derived class to override and call the implementation of the derived class at runtime via a base class pointer or reference.
class Base {
public:
virtual void show() const {
std::cout << "Base class show function" << std::endl;
}
virtual ~Base() {} // virtual destructor,Ensure correct release of derived class resources
};
class Derived : public Base {
public:
void show() const override { // Overrides the base class's show function (math.)
std::cout << "Derived class show function" << std::endl;
}
};
here areoverride
indicates that this is a rewritten function, and if theoverride final
Specifies that a virtual function in a derived class not only overrides a virtual function of the base class, but does not allow further overrides
It is also possible to write purely virtual functions in the base class
Pure virtual function syntax:virtual Return Value Type Function Name (Argument List) = 0 ;
When there are pure virtual functions in a class, the class is also calledabstract class
Abstract Class Features:
- Unable to instantiate an object (that is, unable to declare an object)
- Subclasses must override pure virtual functions in the abstract class, otherwise they also belong to the abstract class
\(Attention:\)
When a member of a derived class occupies space on the heap (that is, the derived class is beingnew
function to get one out of the way), the base class destructor must be a virtual function (purely virtual functions are OK), or else it will result in thedelete
When deriving an object, only the base class's destructor is called, not the derived class's destructor, resulting in a memory leak.
#include <iostream>
//Wrong
class Base {
public:
Base() { std::cout << "Base constructor\n"; }
~Base() { std::cout << "Base destructor\n"; } // nonvirtual destructor
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived constructor\n"; }
~Derived() { std::cout << "Derived destructor\n"; }
};
int main() {
Base* basePtr = new Derived();
delete basePtr; // invoke only Base destructor
return 0;
}
#include <iostream>
//corect
class Base {
public:
Base() { std::cout << "Base constructor\n"; }
virtual ~Base() { std::cout << "Base destructor\n"; } // virtual destructor
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived constructor\n"; }
~Derived() { std::cout << "Derived destructor\n"; }
};
int main() {
Base* basePtr = new Derived();
delete basePtr; // will now call the Derived destructor,then call Base destructor
return 0;
}
Sprinkle finish!!!