← Назад к вопросам

В чём разница между struct и class в C++?

1.3 Junior🔥 151 комментариев
#ООП и проектирование#Структуры данных и алгоритмы#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чём разница между struct и class в C++?

В C++, struct и class практически идентичны с точки зрения функциональности. Основная разница — это видимость по умолчанию. Несмотря на синтаксическое сходство, они имеют важные семантические различия в контексте применения.

Основные различия

Аспектstructclass
Видимость членов по умолчаниюpublicprivate
Видимость наследованияpublicprivate
Когда использоватьПростые структуры данныхСложные объекты с логикой
СемантикаPOD-типы (Plain Old Data)Объекты с инкапсуляцией

Видимость членов по умолчанию

struct Point {
    int x;      // public по умолчанию
    int y;      // public по умолчанию
};

class Person {
    std::string name;  // private по умолчанию
    int age;           // private по умолчанию
};

int main() {
    Point p;
    p.x = 10;     // ОК — public
    p.y = 20;     // ОК — public
    
    Person person;
    // person.name = "John";  // ОШИБКА — private
    // person.age = 30;       // ОШИБКА — private
    
    return 0;
}

Видимость наследования

class Base {
public:
    void publicMethod() { }
protected:
    void protectedMethod() { }
private:
    void privateMethod() { }
};

// Наследование с private (по умолчанию для class)
class DerivedClass : Base {
public:
    void test() {
        // publicMethod();      // ОШИБКА — стал private
        // protectedMethod();   // ОШИБКА — стал private
    }
};

// Наследование с public (по умолчанию для struct)
struct DerivedStruct : Base {
public:
    void test() {
        publicMethod();       // ОК — остался public
        // protectedMethod();  // ОК — остался protected
    }
};

int main() {
    DerivedClass dc;
    // dc.publicMethod();  // ОШИБКА — скрыто из-за private наследования
    
    DerivedStruct ds;
    ds.publicMethod();      // ОК — открыто через public наследование
    
    return 0;
}

Полный пример с явной видимостью

#include <iostream>
#include <string>

// struct для простых данных
struct Point {
    int x;
    int y;
    
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    
    void display() const {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

// class для сложной логики
class Rectangle {
private:
    double width;
    double height;
    
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() const {
        return width * height;
    }
    
    double perimeter() const {
        return 2 * (width + height);
    }
    
    void setWidth(double w) {
        if (w > 0) width = w;
    }
    
    void setHeight(double h) {
        if (h > 0) height = h;
    }
    
    double getWidth() const { return width; }
    double getHeight() const { return height; }
};

int main() {
    // struct — прямой доступ
    Point p(3, 4);
    p.display();  // (3, 4)
    
    // class — через методы
    Rectangle rect(5.0, 10.0);
    std::cout << "Area: " << rect.area() << std::endl;
    std::cout << "Perimeter: " << rect.perimeter() << std::endl;
    
    // Валидация данных
    rect.setWidth(-5);  // Игнорируется
    std::cout << "Width: " << rect.getWidth() << std::endl;
    
    return 0;
}

Наследование: явное задание видимости

class Base {
public:
    void publicBase() { std::cout << "Public Base" << std::endl; }
};

// Явно указываем видимость наследования
class DerivedPublic : public Base {
    // public наследование — открытый интерфейс
};

class DerivedProtected : protected Base {
    // protected наследование — для подклассов
};

class DerivedPrivate : private Base {
    // private наследование — скрывает базовый интерфейс (композиция)
};

int main() {
    DerivedPublic pub;
    pub.publicBase();  // ОК
    
    DerivedProtected prot;
    // prot.publicBase();  // ОШИБКА — стал protected
    
    DerivedPrivate priv;
    // priv.publicBase();  // ОШИБКА — стал private
    
    return 0;
}

Когда использовать struct

1. Простые контейнеры данных (POD типы):

struct Point {
    int x, y;
};

struct Color {
    int r, g, b, a;
};

struct Date {
    int day, month, year;
};

2. Группировка связанных примитивных типов:

struct Rectangle {
    float left, top, right, bottom;
};

struct Vector3 {
    float x, y, z;
};

3. Небольшие, простые структуры без методов:

struct Pair {
    int first;
    int second;
};

Когда использовать class

1. Объекты с инкапсуляцией:

class BankAccount {
private:
    double balance;
    std::string accountNumber;
    
public:
    void deposit(double amount) { /* ... */ }
    bool withdraw(double amount) { /* ... */ }
    double getBalance() const { /* ... */ }
};

2. Полиморфные типы с виртуальными методами:

class Animal {
public:
    virtual void makeSound() = 0;
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void makeSound() override { /* ... */ }
};

3. Сложная логика с валидацией:

class Person {
private:
    std::string name;
    int age;
    
public:
    void setAge(int a) {
        if (a >= 0 && a <= 150) age = a;
    }
};

POD типы (Plain Old Data)

struct часто используется для POD типов:

// POD struct — можно копировать memcpy
struct PODData {
    int a;
    float b;
    char c;
};

// Не POD — содержит нетривиальный деструктор
struct NonPODData {
    std::string str;  // Имеет свой деструктор
    ~NonPODData() { }
};

#include <type_traits>

int main() {
    // Проверка, является ли тип POD
    std::cout << "PODData is POD: " 
              << std::is_trivial<PODData>::value << std::endl;  // 1 (true)
    
    std::cout << "NonPODData is POD: " 
              << std::is_trivial<NonPODData>::value << std::endl;  // 0 (false)
    
    return 0;
}

Практический пример: разница в применении

#include <iostream>
#include <vector>

// struct — для данных, которые просто хранятся
struct StudentRecord {
    int id;
    std::string name;
    float gpa;
};

// class — для объектов с логикой
class GradeManager {
private:
    std::vector<StudentRecord> students;
    float minGPA;
    
public:
    GradeManager(float min = 0.0) : minGPA(min) {}
    
    void addStudent(const StudentRecord& s) {
        if (s.gpa >= minGPA) {
            students.push_back(s);
        }
    }
    
    void displayStudents() const {
        for (const auto& s : students) {
            std::cout << s.name << ": " << s.gpa << std::endl;
        }
    }
    
    float getAverageGPA() const {
        float sum = 0;
        for (const auto& s : students) sum += s.gpa;
        return students.empty() ? 0 : sum / students.size();
    }
};

int main() {
    GradeManager manager(2.0);
    
    manager.addStudent({1, "Alice", 3.8});
    manager.addStudent({2, "Bob", 2.5});
    manager.addStudent({3, "Charlie", 1.5});  // Не добавится
    
    manager.displayStudents();
    std::cout << "Average: " << manager.getAverageGPA() << std::endl;
    
    return 0;
}

Рекомендации

  • Используй struct для простых контейнеров данных (POD типов)
  • Используй class для объектов с логикой и инкапсуляцией
  • Будь консистентен — если используешь struct, не добавляй много методов
  • Явно указывай видимость — не полагайся на умолчания
  • Помни о семантике — выбор влияет на читаемость кода
В чём разница между struct и class в C++? | PrepBro