Diretório de artigos
- 1. Da orientação a processos à orientação a objetos
- 2. A introdução da aula
- 3. Definição de classe
- 4. Qualificador de acesso de classe e encapsulamento
- Cinco. O escopo da aula
- Seis. Instanciação de classe
- Sete. Modelo de objeto de classe
- Oito. este ponteiro
- Nove. Comparação da linguagem C e da pilha de implementação C++
prefácio
Este capítulo iniciará oficialmente
C++
uma série de explicações e este artigo fornecerá uma compreensão preliminar de classes e objetos
1. Da orientação a processos à orientação a objetos
C语言
É orientado ao processo, focando no processo , analisando as etapas para resolver o problema e resolvendo o problema passo a passo por meio de chamadas de funçãoC++
É baseado em orientação a objetos, focando em objetos , dividindo uma coisa em diferentes objetos e contando com a interação entre objetos para ser concluído.
Por exemplo: Para lavar a roupa
C语言
:
C++
:
- Quatro objetos: pessoas, roupas, sabão em pó, máquina de lavar
- O processo de lavagem de roupas: a pessoa coloca as roupas na máquina de lavar, despeja o sabão em pó, liga a máquina de lavar, a máquina de lavar lava as roupas e seca
- Todo o processo é completado pela interação entre os quatro objetos de pessoas, roupas, sabão em pó e máquina de lavar.As pessoas não precisam se preocupar em como a máquina de lavar lava e seca as roupas.
2. A introdução da aula
C语言
Em , somente variáveis podem ser definidas na estrutura . Em , não apenas variáveis podem ser definidasC++
na estrutura , mas também funções podem ser definidas .
Quando implementávamos a estrutura de dados - pilha no passado C语言
, definimos assim:
//C语言
typedef int dataOfStackType;
typedef struct stack
{
dataOfStackType* a;
int top;
int capacity;
}stack;
void StackInit(stack* ps);
void StackPush(stack* ps, dataOfStackType data);
void StackPop(stack* ps);
//...
Considerando que em C++
, podemos defini-lo assim:
//C++
typedef int dataOfStackType;
typedef struct stack
{
void StackInit(stack* ps);
void StackPush(stack* ps, dataOfStackType data);
void StackPop(stack* ps);
//...
dataOfStackType* a;
int top;
int capacity;
}stack;
Como na definição acima, C++中
prefira usar um novo nome - class
em vez disso struct
:
class stack
{
void StackInit(stack* ps);
void StackPush(stack* ps, dataOfStackType data);
void StackPop(stack* ps);
//...
dataOfStackType* a;
int top;
int capacity;
};
3. Definição de classe
1. O que é uma classe
class className
{
//...类的主体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
Como mostrado acima, a definição de uma classe é muito semelhante à definição de uma struct:
class
: Uma palavra-chave que define uma classeclass Name
: o nome da classe- O corpo principal da classe:
{}
o meio é o corpo principal da classe, que consiste em variáveis de membro e funções de membro - Variável de membro: também conhecido como atributo da classe, refere-se à variável definida na classe
- Função de membro: também conhecido como método de classe, refere-se à função definida na classe
2. Definição de classe
Geralmente, existem duas maneiras de definir uma classe:
- A declaração e a definição são colocadas no corpo da classe Nota: Se a função de membro for definida na classe, o compilador pode tratá-la como uma função inline .
//日期类
class Date
{
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
int _year;
int _month;
int _day;
};
- A declaração da classe é colocada no arquivo .h , e a definição da função membro é colocada no arquivo .cpp Nota: o nome da classe precisa ser adicionado antes do nome da função membro
::
.
//Date.h文件中声明类
//日期类
class Date
{
public:
void Init(int year, int month, int day);
void Print();
int _year;
int _month;
int _day;
};
//Date.c文件中定义成员函数
#include"Date.h"
void Date::Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Date::Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Em geral, a segunda abordagem é mais desejável.
3. Sugestões para regras de nomenclatura de variáveis de membros
Vejamos a seguinte classe:
class Date
{
public:
void Init(int year)
{
//这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
As variáveis acima são complicadas? São variáveis de membro ou parâmetros de função? Para evitar confusão, recomendamos nomear as variáveis de membro da seguinte maneira Init函数
:year
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样
class Date
{
public:
void Init(int year)
{
mYear = year;
}
private:
int mYear;
};
Outros métodos também são possíveis, dependendo dos requisitos da empresa. Geralmente, basta adicionar um prefixo ou sufixo para identificar a diferença.
4. Qualificador de acesso de classe e encapsulamento
1. Qualificadores de acesso
Definimos várias funções de membro e variáveis de membro na classe. Às vezes, não queremos que outros acessem alguns membros da classe casualmente, como variáveis de membro, mas outros membros estão abertos ao mundo externo, como funções de membro. Então, precisamos usar qualificadores de acesso para modificar esses membros .
Existem três qualificadores de acesso: public
, protected
,private
Descrição do qualificador de acesso :
class
As permissões de acesso padrão sãoprivate
,struct
parapublic
(devido àstruct
compatibilidadeC
)public
Membros modificados podem ser acessados diretamente fora da classeprotected
e membros modificados não podem ser acessados diretamenteprivate
fora da classe (aqui e são semelhantes)protected
private
- Os escopos de acesso começam na ocorrência deste qualificador de acesso até a próxima ocorrência do qualificador de acesso
- Se não houver qualificador de acesso atrás, o escopo termina no
}
final da classe
Nota: Os qualificadores de acesso são úteis apenas em tempo de compilação , quando os dados são mapeados para a memória, não há diferença nos qualificadores de acesso.
Então a pergunta é: qual é a diferença entre c++
neutralização struct
e neutralização?class
Resposta:
C++
Ele precisa ser compatívelC语言
, então nãoC++
sóstruct
pode ser usado como uma estrutura ,C++
masstruct
também pode ser usado para definir uma classe , queclass
é o mesmo que definir uma classe. A diferença é que a permissão de acesso padrãostruct
da classe definida é , e a permissão de acesso padrão da classe definida é .public
class
private
2. Encapsulamento
A orientação a objetos tem três características: encapsulamento, herança e polimorfismo.
No estágio atual, aprendemos apenas os recursos do pacote. Então, o que é encapsulamento?
Encapsulamento: combine organicamente dados e métodos de manipulação de dados , oculte propriedades de objetos e detalhes de implementação e apenas exponha interfaces para interagir com objetos
O encapsulamento é essencialmente um tipo de gerenciamento, facilitando o uso de classes pelos usuários :
- Por exemplo: para um dispositivo tão complexo como um computador, a única coisa fornecida ao usuário é o botão liga / desliga, entrada de teclado, monitor,
USB
conector etc., permitindo que o usuário interaja com o computador e conclua os assuntos diários. Mas, na verdade, o verdadeiro trabalho do computador são alguns componentes de hardware, como CPU, placa gráfica e memória. - Para usuários de computador, eles não precisam se preocupar com os componentes principais internos , como a disposição dos circuitos da placa-mãe, como a CPU é projetada por dentro etc. Portanto, quando o fabricante do computador sai da fábrica, ele coloca um shell do lado de fora para ocultar os detalhes internos da implementação e fornece apenas interruptores externos, mouse e teclado, etc., para que os usuários possam interagir com o computador .
- O encapsulamento na
C++
linguagem pode combinar organicamente dados e métodos de manipulação de dados por meio de classes, ocultar os detalhes de implementação interna de objetos por meio de direitos de acesso e controlar quais métodos podem ser usados diretamente fora da classe .
Cinco. O escopo da aula
Uma classe define um novo escopo e todos os membros da classe estão no escopo da classe. Ao definir membros fora da classe, você precisa usar ::
o operador de escopo para indicar a qual domínio de classe o membro pertence :
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
}
Seis. Instanciação de classe
O processo de criação de um objeto com uma classe é chamado de instanciação da classe:
//类的声明
class Date
{
public:
//...
int _year;
int _month;
int _day;
};
int main()
{
//类的实例化对象
Date d1;
Date d2;
Date d3;
return 0;
}
A instanciação de classe de objetos é como construir uma casa usando desenhos de projeto na realidade . Classes são como desenhos de projeto . Somente o que é necessário é projetado, mas não há construção física. O objeto instanciado é a casa real que pode realmente armazenar dados e ocupar espaço físico.
Como um desenho de projeto, não é que um desenho de projeto possa construir apenas uma casa, e uma classe também pode instanciar vários objetos.
Sete. Modelo de objeto de classe
Uma classe pode ter variáveis de membro e funções de membro, então como calcular o tamanho de um objeto de classe?
Como mencionado acima, C++
classes e estruturas são essencialmente as mesmas. Para ser compatível C语言
, C++
serão seguidas as regras de cálculo do tamanho da estrutura , sendo que a classe é a mesma da estrutura, portanto a classe continuará utilizando as mesmas regras de cálculo da estrutura .
Observação: as funções de membro estão no segmento de código público , portanto, as funções de membro não ocupam espaço . Portanto, o tamanho de uma classe é, na verdade, o tamanho das variáveis membros da classe, seguindo as regras de alinhamento de memória.
Vamos comprová-lo:
// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1() {
}
private:
int _a;
char _b;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {
}
};
// 类中什么都没有---空类
class A3
{
};
int main()
{
cout << sizeof(A1) << endl;
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
return 0;
}
resultado da operação:
Conforme mostrado na figura acima, o resultado é consistente com a conclusão: o tamanho da classe calcula apenas o tamanho das variáveis de membro na classe e segue as regras de alinhamento de memória .
- Então, por que o tamanho da classe vazia
1
?
Isso ocorre porque a classe vazia é especial e o compilador fornece à classe vazia um byte para identificar exclusivamente os objetos dessa classe .
Suplemento: Regras de alinhamento de memória de estrutura :
- O primeiro membro está no endereço da estrutura em offset .
0
- Outras variáveis de membro devem ser alinhadas a um endereço que seja um múltiplo inteiro de um determinado número (número de alinhamento) . Nota: Alinhamento = o menor valor entre o alinhamento padrão do compilador e o tamanho do membro . O número de alinhamento padrão no VS é 8
- O tamanho total da estrutura é: um múltiplo inteiro do número de alinhamento máximo (o maior de todos os tipos de variáveis e o menor parâmetro de alinhamento padrão) .
- Se uma estrutura for aninhada, a estrutura aninhada será alinhada a um múltiplo inteiro de seu próprio alinhamento máximo e o tamanho geral da estrutura será um múltiplo inteiro de todos os alinhamentos máximos (incluindo o alinhamento da estrutura aninhada) .
Oito. este ponteiro
1. Conheça este ponteiro
Vamos definir uma classe de data primeiro Date
:
//日期类
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2;
d1.Init(2022, 1, 11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
Para a operação acima, existe a seguinte questão:
Date
Existem duas funções de membroInit
e na classe , e não há distinção entre objetos diferentes no corpo da função , portanto, quando a função é chamada , como a função sabe que o objeto deve ser definido em vez do objeto?Print
d1
Init
d1
d2
C++
this指针
Resolva este problema introduzindo :
C++
O compilador adiciona um parâmetro de ponteiro oculto a cada função de membro não estática , fazendo com que o ponteiro aponte para o objeto atual (o objeto que chama a função quando a função está em execução) e todas as operações de variável de membro no corpo da função são acessadas por meio desse ponteiro . Só que todas as operações são transparentes para o usuário, ou seja, o usuário não precisa passar, e o compilador completa automaticamente .
2. Características deste ponteiro
this
Os ponteiros têm as seguintes propriedades :
this
O tipo do ponteiro: o tipo* const
, ou seja, na função de membro, não pode atribuirthis
um valor ao ponteiro
void Init(int year, int month, int day)
{
//错误示例
this=nullptr;
}
- Só pode ser usado dentro de funções de membro
this
Um ponteiro é essencialmente um parâmetro formal de uma função de membro.Quando um objeto chama uma função de membro, o endereço do objeto é passado para o parâmetro formal como umthis
parâmetro real . Portanto, nenhum ponteiro é armazenado no objetothis
.this
O ponteiro é o primeiro parâmetro de ponteiro implícito da função de membro.Geralmente , ele é passado automaticamente pelo compilador atravésecx
do registro e não precisa ser passado pelo usuário.
Com this
ponteiros, a classe de data que implementamos acima também pode ser implementada assim:
//日期类
class Date
{
public:
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout <<this-> _year << "-" <<this-> _month << "-" <<this-> _day << endl;
}
private:
int _year;
int _month;
int _day;
};
Mas não há necessidade de escrever isso no processo de escrever o código, porque o compilador já fez isso por nós, então não queremos fazer coisas ingratas.
【Questões de entrevista】
this
Onde existem ponteiros ?
Resposta: Este ponteiro é um parâmetro formal de uma função de membro, portanto, há uma área de pilha .this
Um ponteiro pode ser nulo ?
Observe os dois trechos de código a seguir:
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
// 2.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
Escolha C para o primeiro curso e B para o segundo curso, porque:
P
CallPrint
, não será desreferenciado, pois o endereçoPrint
de não está no objeto ,P
passado como argumento parathis
o ponteiro- Programa 1:
this
O ponteiro é nulo, mas a função não desreferenciathis
o ponteiro - O segundo programa:
this
o ponteiro está vazio, mas o acesso dentro da função_a
, a essência éthis->_a
, ou seja, desreferenciar o ponteiro nulo, e ocorrerá um erro de operação.
Nove. Comparação da linguagem C e da pilha de implementação C++
1. Implementação da linguagem C
typedef int DataType;
typedef struct Stack
{
DataType* array;
int capacity;
int size;
}Stack;
void StackInit(Stack* ps)
{
assert(ps);
ps->array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == ps->array)
{
assert(0);
return;
}
ps->capacity = 3;
ps->size = 0;
}
void StackDestroy(Stack* ps)
{
assert(ps);
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
void CheckCapacity(Stack* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity * 2;
DataType* temp = (DataType*)realloc(ps->array,
newcapacity * sizeof(DataType));
if (temp == NULL)
{
perror("realloc申请空间失败!!!");
return;
}
ps->array = temp;
ps->capacity = newcapacity;
}
}
void StackPush(Stack* ps, DataType data)
{
assert(ps);
CheckCapacity(ps);
ps->array[ps->size] = data;
ps->size++;
}
int StackEmpty(Stack* ps)
{
assert(ps);
return 0 == ps->size;
}
void StackPop(Stack* ps)
{
if (StackEmpty(ps))
return;
ps->size--;
}
DataType StackTop(Stack* ps)
{
assert(!StackEmpty(ps));
return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->size;
}
int main()
{
Stack s;
StackInit(&s);
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
printf("%d\n", StackTop(&s));
printf("%d\n", StackSize(&s));
StackPop(&s);
StackPop(&s);
printf("%d\n", StackTop(&s));
printf("%d\n", StackSize(&s));
StackDestroy(&s);
return 0;
}
Pode-se ver que, quando implementadas na linguagem C, as funções de operação relacionadas à pilha têm o seguinte em comum:
- O primeiro parâmetro de cada função é
Stack*
- O primeiro parâmetro deve ser detectado na função , pois o parâmetro pode ser
NULL
- Na função,
Stack*
a pilha é manipulada por meio de parâmetros. - O endereço da
Stack
variável de estrutura deve ser passado ao chamar
Na estrutura pode ser definida apenas a estrutura de armazenamento dos dados , não podendo ser colocado na estrutura o método de operação dos dados, ou seja, os dados e a forma de operação dos dados são separados , e a implementação é um pouco mais complicada, envolvendo um grande número de operações de ponteiro, podendo ocorrer erros caso não tenha cuidado.
2. Implementação C++
typedef int DataType;
class Stack
{
public:
void Init()
{
_array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = 3;
_size = 0;
}
void Push(DataType data)
{
CheckCapacity();
_array[_size] = data;
_size++;
}
void Pop()
{
if (Empty())
return;
_size--;
}
DataType Top() {
return _array[_size - 1]; }
int Empty() {
return 0 == _size; }
int Size() {
return _size; }
void Destroy()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
void CheckCapacity()
{
if (_size == _capacity)
{
int newcapacity = _capacity * 2;
DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
if (temp == NULL)
{
perror("realloc申请空间失败!!!");
return;
}
_array = temp;
_capacity = newcapacity;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
int main()
{
Stack s;
s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
printf("%d\n", s.Top());
printf("%d\n", s.Size());
s.Pop();
s.Pop();
printf("%d\n", s.Top());
printf("%d\n", s.Size());
s.Destroy();
return 0;
}
C++
As classes podem ser usadas para combinar perfeitamente dados e métodos de manipulação de dados , e os direitos de acesso podem ser usados para controlar os métodos que podem ser chamados fora da classe , ou seja, encapsulamento , que está mais de acordo com a cognição humana de uma coisa, assim como usar seus próprios membros . Além disso, cada método não precisa passar parâmetros , e os parâmetros serão restaurados automaticamente após a compilação do compilador, ou seja, os parâmetros são mantidos pelo compilador e os parâmetros precisam ser mantidos pelo usuário.Stack*
C++
Stack *
C语言
Este é o final deste artigo, o texto do código não é fácil, por favor, me apoie muito!