Введение
В этом лабораторном занятии (lab) вы научитесь создавать классы и объекты в программировании на языке C++. Вы определите классы с приватными (private) данными-членами, реализуете публичные (public) методы-члены, создадите конструкторы с разными параметрами, напишете деструкторы для очистки ресурсов, используете спецификаторы доступа, реализуете наследование между классами, переопределите методы базового класса, создадите дружественные (friend) функции для контроля доступа и используете статические (static) члены и методы. Эти концепции являются фундаментальными для объектно-ориентированного программирования на C++ и предоставят прочный фундамент для создания более сложных приложений.
Определение класса с приватными членами данных
Класс в C++ представляет собой шаблон для создания объектов. Он определяет набор свойств (данных-членов) и методов (функций-членов), которые будут у объектов, созданных на основе этого класса. Классы помогают организовать код и реализовать принципы объектно-ориентированного программирования, такие как инкапсуляция, наследование и полиморфизм.
Базовый синтаксис определения класса в C++ выглядит следующим образом:
class ClassName {
private:
// Private data members
int dataMember1;
std::string dataMember2;
public:
// Public member functions
void memberFunction1();
int memberFunction2();
};
В C++ есть три спецификатора доступа, которые контролируют видимость и доступность членов класса:
private: Члены, объявленные как private, доступны только внутри класса.public: Члены, объявленные как public, доступны извне класса.protected: Члены, объявленные как protected, доступны внутри класса и экземплярами производных классов.
На этом этапе вы научитесь определять класс с приватными данными-членами в C++. Приватные данные-члены - это важная концепция объектно-ориентированного программирования, которая помогает инкапсулировать и защитить внутреннее состояние объекта.
Сначала откройте WebIDE и перейдите в каталог ~/project. Создайте новый файл с именем student.cpp:
touch ~/project/student.cpp
Откройте файл student.cpp в WebIDE и добавьте следующий код для определения класса Student с приватными данными-членами:
#include <iostream>
#include <string>
class Student {
private:
// Private data members
std::string name;
int age;
double gpa;
public:
// We'll add methods to interact with these private members in later steps
void displayInfo() {
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "GPA: " << gpa << std::endl;
}
};
int main() {
Student student;
// Note: We can't directly access private members
// student.name = "John"; // This would cause a compilation error
student.displayInfo();
return 0;
}
Разберем основные концепции:
Приватные данные-члены:
- Объявляются с использованием спецификатора доступа
private: - Не могут быть напрямую доступны извне класса
- Предоставляют защиту данных и инкапсуляцию
- Объявляются с использованием спецификатора доступа
Типы данных:
std::string name: Хранит имя студентаint age: Хранит возраст студентаdouble gpa: Хранит средний балл студента
Инкапсуляция:
- Приватные члены могут быть доступны только через публичные методы
- Предотвращает прямое изменение внутренних данных
Скомпилируйте программу:
g++ student.cpp -o student
./student
Пример вывода при запуске программы:
Name:
Age: 0
GPA: 0
Основные моменты:
- Приватные члены скрыты от внешнего доступа
- Они могут быть изменены только через методы класса
- Это помогает сохранить целостность и контроль данных
Реализация публичных методов-членов
На этом этапе вы научитесь реализовывать публичные методы-члены в C++. Публичные методы-члены позволяют контролировать доступ к приватным (private) данным-членам и предоставляют способы взаимодействия с объектами класса.
Откройте файл student.cpp в WebIDE и измените класс Student, добавив в него публичные методы-члены:
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int age;
double gpa;
public:
// Setter methods to modify private data members
void setName(std::string studentName) {
name = studentName;
}
void setAge(int studentAge) {
if (studentAge > 0 && studentAge < 120) {
age = studentAge;
} else {
std::cout << "Invalid age!" << std::endl;
}
}
void setGPA(double studentGPA) {
if (studentGPA >= 0.0 && studentGPA <= 4.0) {
gpa = studentGPA;
} else {
std::cout << "Invalid GPA!" << std::endl;
}
}
// Getter methods to access private data members
std::string getName() {
return name;
}
int getAge() {
return age;
}
double getGPA() {
return gpa;
}
// Display method to print student information
void displayInfo() {
std::cout << "Student Information:" << std::endl;
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "GPA: " << gpa << std::endl;
}
};
int main() {
Student student;
// Using public member functions to set and get data
student.setName("Alice Johnson");
student.setAge(20);
student.setGPA(3.75);
// Display student information
student.displayInfo();
// Demonstrate getter methods
std::cout << "\nStudent Name: " << student.getName() << std::endl;
std::cout << "Student Age: " << student.getAge() << std::endl;
std::cout << "Student GPA: " << student.getGPA() << std::endl;
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Student Information:
Name: Alice Johnson
Age: 20
GPA: 3.75
Student Name: Alice Johnson
Student Age: 20
Student GPA: 3.75
Основные моменты о публичных методах-членах:
- Предоставляют контролируемый доступ к приватным данным-членам
- Методы-сеттеры (setters) позволяют изменять данные с валидацией
- Методы-геттеры (getters) позволяют читать приватные данные
- Помогают сохранить инкапсуляцию и целостность данных
Создание конструкторов с разными параметрами
На этом этапе вы научитесь создавать конструкторы с разными параметрами в C++. Конструкторы - это специальные методы-члены, которые инициализируют объекты при их создании.
Откройте файл student.cpp в WebIDE и измените класс Student, добавив в него несколько конструкторов:
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int age;
double gpa;
public:
// Default constructor
Student() {
name = "Unknown";
age = 0;
gpa = 0.0;
}
// Constructor with name parameter
Student(std::string studentName) {
name = studentName;
age = 0;
gpa = 0.0;
}
// Constructor with name and age parameters
Student(std::string studentName, int studentAge) {
name = studentName;
age = studentAge;
gpa = 0.0;
}
// Full constructor with all parameters
Student(std::string studentName, int studentAge, double studentGPA) {
name = studentName;
age = studentAge;
gpa = studentGPA;
}
// Display method to print student information
void displayInfo() {
std::cout << "Student Information:" << std::endl;
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "GPA: " << gpa << std::endl;
}
};
int main() {
// Using different constructors
Student student1; // Default constructor
Student student2("Alice Johnson"); // Constructor with name
Student student3("Bob Smith", 22); // Constructor with name and age
Student student4("Charlie Brown", 20, 3.75); // Full constructor
std::cout << "Student 1:" << std::endl;
student1.displayInfo();
std::cout << "\nStudent 2:" << std::endl;
student2.displayInfo();
std::cout << "\nStudent 3:" << std::endl;
student3.displayInfo();
std::cout << "\nStudent 4:" << std::endl;
student4.displayInfo();
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Student 1:
Student Information:
Name: Unknown
Age: 0
GPA: 0
Student 2:
Student Information:
Name: Alice Johnson
Age: 0
GPA: 0
Student 3:
Student Information:
Name: Bob Smith
Age: 22
GPA: 0
Student 4:
Student Information:
Name: Charlie Brown
Age: 20
GPA: 3.75
Основные моменты о конструкторах:
- Конструкторы имеют то же имя, что и класс
- Они инициализируют данные-члены объекта
- Может быть несколько конструкторов с разными параметрами
- Вызываются автоматически при создании объекта
Написание деструктора для очистки ресурсов
На этом этапе вы узнаете о деструкторах в C++, которые представляют собой специальные методы-члены, ответственные за очистку ресурсов при уничтожении объекта. Мы покажем, как написать деструктор и поймем его важность в управлении памятью.
Откройте файл student.cpp в WebIDE и измените код, добавив деструктор и динамическое выделение памяти:
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int age;
double gpa;
char* dynamicBuffer; // Simulating dynamic memory allocation
public:
// Constructors
Student() {
name = "Unknown";
age = 0;
gpa = 0.0;
dynamicBuffer = new char[50]; // Allocate dynamic memory
std::cout << "Default Constructor Called" << std::endl;
}
Student(std::string studentName) : name(studentName), age(0), gpa(0.0) {
dynamicBuffer = new char[50];
std::cout << "Parameterized Constructor Called" << std::endl;
}
// Destructor
~Student() {
// Clean up dynamically allocated memory
delete[] dynamicBuffer;
std::cout << "Destructor Called for " << name << std::endl;
}
void displayInfo() {
std::cout << "Student Information:" << std::endl;
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "GPA: " << gpa << std::endl;
}
};
int main() {
// Demonstrating object creation and destruction
{
std::cout << "Creating first student:" << std::endl;
Student student1("Alice Johnson");
student1.displayInfo();
std::cout << "\nCreating second student:" << std::endl;
Student student2("Bob Smith");
student2.displayInfo();
} // Objects go out of scope here, destructors are called
std::cout << "\nExiting main function" << std::endl;
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Creating first student:
Parameterized Constructor Called
Student Information:
Name: Alice Johnson
Age: 0
GPA: 0
Creating second student:
Parameterized Constructor Called
Student Information:
Name: Bob Smith
Age: 0
GPA: 0
Destructor Called for Bob Smith
Destructor Called for Alice Johnson
Exiting main function
Основные моменты о деструкторах:
- Определяются с использованием символа
~, за которым следует имя класса - Вызываются автоматически при уничтожении объекта
- Используются для освобождения динамически выделенных ресурсов
- Помогают предотвратить утечки памяти
- Не имеют возвращаемого типа и параметров
Важные характеристики деструкторов:
- Вызываются, когда объект выходит из области видимости
- Автоматически вызываются компилятором
- Ключевые для очистки динамической памяти
- Помогают эффективно управлять системными ресурсами
Использование спецификаторов доступа (public, private, protected)
На этом этапе вы узнаете о спецификаторах доступа в C++ и о том, как они контролируют видимость и доступность членов класса. Мы рассмотрим различия между уровнями доступа public, private и protected.
Откройте файл student.cpp в WebIDE и измените код, чтобы продемонстрировать использование спецификаторов доступа:
#include <iostream>
#include <string>
class Student {
private:
// Private members: accessible only within the class
std::string name;
int age;
double gpa;
protected:
// Protected members: accessible within the class and its derived classes
std::string school;
public:
// Public members: accessible from anywhere
Student(std::string studentName, int studentAge, double studentGPA) {
name = studentName;
age = studentAge;
gpa = studentGPA;
school = "Default School";
}
// Public method to demonstrate access to private members
void displayInfo() {
std::cout << "Student Information:" << std::endl;
std::cout << "Name: " << name << std::endl; // Accessing private member
std::cout << "Age: " << age << std::endl;
std::cout << "GPA: " << gpa << std::endl;
std::cout << "School: " << school << std::endl;
}
// Public method to modify private members
void updateGPA(double newGPA) {
if (newGPA >= 0.0 && newGPA <= 4.0) {
gpa = newGPA;
}
}
};
class GraduateStudent : public Student {
public:
GraduateStudent(std::string name, int age, double gpa)
: Student(name, age, gpa) {
// Can access protected 'school' member from base class
school = "Graduate School";
}
void displaySchool() {
std::cout << "School: " << school << std::endl;
}
};
int main() {
Student student("Alice Johnson", 20, 3.75);
student.displayInfo(); // Public method accessing private members
// Uncommenting the lines below would cause compilation errors
// student.name = "John"; // Error: Cannot access private member
// student.age = 25; // Error: Cannot access private member
student.updateGPA(3.90); // Modifying private member through public method
student.displayInfo();
GraduateStudent gradStudent("Bob Smith", 25, 3.90);
gradStudent.displayInfo();
gradStudent.displaySchool();
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Student Information:
Name: Alice Johnson
Age: 20
GPA: 3.75
School: Default School
Student Information:
Name: Alice Johnson
Age: 20
GPA: 3.90
School: Default School
Student Information:
Name: Bob Smith
Age: 25
GPA: 3.90
School: Graduate School
School: Graduate School
Основные моменты о спецификаторах доступа:
private: Члены доступны только внутри того же классаprotected: Члены доступны внутри класса и его производных классовpublic: Члены доступны из любого места- Помогают контролировать доступ к данным и сохранять инкапсуляцию
Реализация наследования между классами
На этом этапе вы узнаете о наследовании в C++, фундаментальном концепте объектно-ориентированного программирования, которое позволяет создавать новые классы на основе существующих.
Откройте файл student.cpp в WebIDE и измените код, чтобы продемонстрировать наследование:
#include <iostream>
#include <string>
// Base class
class Person {
protected:
std::string name;
int age;
public:
// Constructor
Person(std::string personName, int personAge) {
name = personName;
age = personAge;
}
// Method to display basic information
void displayInfo() {
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
}
};
// Derived class: Student inherits from Person
class Student : public Person {
private:
double gpa;
std::string major;
public:
// Constructor that calls base class constructor
Student(std::string studentName, int studentAge, double studentGPA, std::string studentMajor)
: Person(studentName, studentAge) {
gpa = studentGPA;
major = studentMajor;
}
// Additional method specific to Student
void displayStudentInfo() {
// Calling base class method
displayInfo();
std::cout << "GPA: " << gpa << std::endl;
std::cout << "Major: " << major << std::endl;
}
};
// Another derived class: Employee inherits from Person
class Employee : public Person {
private:
std::string company;
double salary;
public:
// Constructor that calls base class constructor
Employee(std::string employeeName, int employeeAge, std::string employeeCompany, double employeeSalary)
: Person(employeeName, employeeAge) {
company = employeeCompany;
salary = employeeSalary;
}
// Additional method specific to Employee
void displayEmployeeInfo() {
// Calling base class method
displayInfo();
std::cout << "Company: " << company << std::endl;
std::cout << "Salary: $" << salary << std::endl;
}
};
int main() {
// Creating objects of derived classes
Student student("Alice Johnson", 20, 3.75, "Computer Science");
std::cout << "Student Information:" << std::endl;
student.displayStudentInfo();
std::cout << "\n";
Employee employee("Bob Smith", 35, "Tech Corp", 75000.0);
std::cout << "Employee Information:" << std::endl;
employee.displayEmployeeInfo();
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Student Information:
Name: Alice Johnson
Age: 20
GPA: 3.75
Major: Computer Science
Employee Information:
Name: Bob Smith
Age: 35
Company: Tech Corp
Salary: $75000
Основные моменты о наследовании:
- Базовый класс (Person) содержит общие атрибуты
- Производные классы (Student, Employee) наследуются от базового класса
- Используйте
: public BaseClassNameдля наследования - Можно добавлять новые атрибуты и методы в производных классах
- Можно использовать методы базового класса в производных классах
Переопределение методов базового класса
На этом этапе вы узнаете, как переопределять методы базового класса в C++, что позволяет производным классам предоставлять свою собственную реализацию методов, унаследованных от базового класса.
Откройте файл student.cpp в WebIDE и измените код, чтобы продемонстрировать переопределение методов:
#include <iostream>
#include <string>
class Animal {
protected:
std::string name;
public:
Animal(std::string animalName) : name(animalName) {}
// Virtual keyword allows method to be overridden
virtual void makeSound() {
std::cout << "Some generic animal sound" << std::endl;
}
// Virtual method for displaying information
virtual void displayInfo() {
std::cout << "Animal Name: " << name << std::endl;
}
};
class Dog : public Animal {
private:
std::string breed;
public:
Dog(std::string dogName, std::string dogBreed)
: Animal(dogName), breed(dogBreed) {}
// Override makeSound method
void makeSound() override {
std::cout << "Woof! Woof!" << std::endl;
}
// Override displayInfo method
void displayInfo() override {
// Call base class method first
Animal::displayInfo();
std::cout << "Breed: " << breed << std::endl;
}
};
class Cat : public Animal {
private:
std::string color;
public:
Cat(std::string catName, std::string catColor)
: Animal(catName), color(catColor) {}
// Override makeSound method
void makeSound() override {
std::cout << "Meow! Meow!" << std::endl;
}
// Override displayInfo method
void displayInfo() override {
// Call base class method first
Animal::displayInfo();
std::cout << "Color: " << color << std::endl;
}
};
int main() {
// Create animal objects
Animal genericAnimal("Generic Animal");
Dog myDog("Buddy", "Labrador");
Cat myCat("Whiskers", "Orange");
// Demonstrate polymorphic behavior
std::cout << "Generic Animal:" << std::endl;
genericAnimal.displayInfo();
genericAnimal.makeSound();
std::cout << "\nDog:" << std::endl;
myDog.displayInfo();
myDog.makeSound();
std::cout << "\nCat:" << std::endl;
myCat.displayInfo();
myCat.makeSound();
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Generic Animal:
Animal Name: Generic Animal
Some generic animal sound
Dog:
Animal Name: Buddy
Breed: Labrador
Woof! Woof!
Cat:
Animal Name: Whiskers
Color: Orange
Meow! Meow!
Основные моменты о переопределении методов:
- Используйте ключевое слово
virtualв базовом классе, чтобы разрешить переопределение - Используйте ключевое слово
overrideв производном классе, чтобы явно переопределить метод - Можно вызвать метод базового класса с помощью
BaseClass::method() - Позволяет иметь разные реализации для одного и того же метода
- Обеспечивает полиморфное поведение
Создание дружественных функций для контроля доступа
На этом этапе вы узнаете о дружественных функциях в C++, которые позволяют внешним функциям или другим классам получать доступ к приватным и защищенным членам класса.
Откройте файл student.cpp в WebIDE и измените код, чтобы продемонстрировать использование дружественных функций:
#include <iostream>
#include <string>
class BankAccount {
private:
std::string accountHolder;
double balance;
// Declare friend functions
friend void displayAccountDetails(const BankAccount& account);
friend class AccountManager;
public:
// Constructor
BankAccount(std::string name, double initialBalance) {
accountHolder = name;
balance = initialBalance;
}
// Method to deposit money
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
};
// Friend function that can access private members
void displayAccountDetails(const BankAccount& account) {
std::cout << "Account Holder: " << account.accountHolder << std::endl;
std::cout << "Current Balance: $" << account.balance << std::endl;
}
// Friend class that can access private members
class AccountManager {
public:
// Method that can access private members of BankAccount
void transferFunds(BankAccount& from, BankAccount& to, double amount) {
if (from.balance >= amount) {
from.balance -= amount;
to.balance += amount;
std::cout << "Transfer successful!" << std::endl;
} else {
std::cout << "Insufficient funds!" << std::endl;
}
}
};
int main() {
// Create bank accounts
BankAccount account1("Alice Johnson", 1000.0);
BankAccount account2("Bob Smith", 500.0);
// Use friend function to display account details
std::cout << "Account 1 Details:" << std::endl;
displayAccountDetails(account1);
std::cout << "\nAccount 2 Details:" << std::endl;
displayAccountDetails(account2);
// Use friend class to transfer funds
AccountManager manager;
std::cout << "\nTransferring $200 from Alice to Bob:" << std::endl;
manager.transferFunds(account1, account2, 200.0);
std::cout << "\nUpdated Account Details:" << std::endl;
displayAccountDetails(account1);
displayAccountDetails(account2);
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
Account 1 Details:
Account Holder: Alice Johnson
Current Balance: $1000
Account 2 Details:
Account Holder: Bob Smith
Current Balance: $500
Transferring $200 from Alice to Bob:
Transfer successful!
Updated Account Details:
Account Holder: Alice Johnson
Current Balance: $800
Account Holder: Bob Smith
Current Balance: $700
Основные моменты о дружественных функциях:
- Объявляются с использованием ключевого слова
friend - Можно получить доступ к приватным и защищенным членам класса
- Объявляются внутри определения класса
- Может быть отдельной функцией или целым классом
- Нарушает инкапсуляцию, поэтому используйте с осторожностью
Использование статических членов и методов
На этом этапе вы узнаете о статических членах и методах в C++, которые являются общими для всех экземпляров класса и принадлежат самому классу, а не отдельным объектам.
Откройте файл student.cpp в WebIDE и измените код, чтобы продемонстрировать использование статических членов и методов:
#include <iostream>
#include <string>
class University {
private:
std::string name;
static int totalStudents; // Static member variable
static double totalTuition; // Another static member variable
public:
// Constructor
University(std::string universityName) : name(universityName) {}
// Method to add students
void addStudents(int count) {
totalStudents += count;
}
// Method to add tuition
void addTuition(double amount) {
totalTuition += amount;
}
// Static method to display total students
static void displayTotalStudents() {
std::cout << "Total Students Across All Universities: "
<< totalStudents << std::endl;
}
// Static method to display total tuition
static void displayTotalTuition() {
std::cout << "Total Tuition Collected: $"
<< totalTuition << std::endl;
}
// Static method to calculate average tuition
static double calculateAverageTuition() {
return (totalStudents > 0)?
(totalTuition / totalStudents) : 0.0;
}
};
// Initialize static member variables outside the class
int University::totalStudents = 0;
double University::totalTuition = 0.0;
int main() {
// Create university objects
University harvard("Harvard University");
University mit("MIT");
University stanford("Stanford University");
// Add students and tuition
harvard.addStudents(5000);
harvard.addTuition(75000000.0);
mit.addStudents(4500);
mit.addTuition(65000000.0);
stanford.addStudents(4200);
stanford.addTuition(60000000.0);
// Call static methods directly on the class
std::cout << "University Statistics:" << std::endl;
University::displayTotalStudents();
University::displayTotalTuition();
// Calculate and display average tuition
std::cout << "Average Tuition per Student: $"
<< University::calculateAverageTuition() << std::endl;
return 0;
}
Скомпилируйте и запустите программу:
g++ student.cpp -o student
./student
Пример вывода:
University Statistics:
Total Students Across All Universities: 13700
Total Tuition Collected: $2e+08
Average Tuition per Student: $14598.5
Основные моменты о статических членах и методах:
- Общие для всех экземпляров класса
- Объявляются с использованием ключевого слова
static - Можно обращаться к ним без создания объекта
- Принадлежат классу, а не отдельным объектам
- Полезны для отслеживания информации, относящейся ко всему классу
Резюме
В этом практическом занятии вы научились определять класс с приватными членами данных, что является важной концепцией в объектно-ориентированном программировании. Приватные члены данных помогают инкапсулировать и защитить внутреннее состояние объекта. Вы также узнали о важности публичных методов-членов, которые позволяют контролировать доступ к приватным членам данных. Это помогает сохранить целостность и контроль данных. Кроме того, вы изучили использование конструкторов, деструкторов, спецификаторов доступа, наследования, переопределения методов, дружественных функций, а также статических членов и методов, все это - фундаментальные принципы объектно-ориентированного программирования в C++.



