minhas informações de contato
Correspondência[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
No capítulo anterior, mencionamos brevemente alguns conceitos de classes e objetos. Agora, vamos revisá-los juntos.
class é a palavra-chave da classe definidora, seguida pelo nome da classe definidora, {} é o corpo principal da classe, seguido por ";", as funções de membro definidas na classe são expandidas por padrão (inline)
Um formato de encapsulamento, dividido em três tipos: público, privado e protegido. O escopo de permissão de acesso começa na posição onde o qualificador de acesso aparece até que o próximo qualificador de acesso apareça. Se não houver nenhum qualificador de acesso subsequente, ele terminará. em } ou seja, classe.
A classe define um novo escopo. 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.
A seguir está o longa-metragem ///
• O processo de criação de um objeto na memória física usando um tipo de classe é chamado de instanciação de classe.
• Uma classe é uma descrição abstrata de um objeto. É como um modelo. Ela limita apenas as variáveis-membro.
É uma declaração e nenhum espaço é alocado. O espaço será alocado somente quando um objeto for instanciado usando uma classe.
• Uma classe pode instanciar vários objetos. Os objetos instanciados ocupam espaço físico real e armazenam variáveis de membros da classe.
Resumindo em uma frase: somente quando os objetos forem instanciados com classes irão alocar espaço (assim como construir uma casa com desenhos irá ocupar espaço).
- 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;
- };
No código acima, as variáveis-membro em private são apenas definidas e nenhum novo espaço é aberto;
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
Dois novos objetos são instanciados, então um novo espaço é aberto.
• O primeiro membro está no deslocamento de endereço 0 da estrutura.
• Outras variáveis-membro devem ser mapeadas para endereços que sejam múltiplos inteiros de um determinado número (logaritmo).
• Nota: logaritmo = o menor entre o logaritmo padrão do compilador e o tamanho do membro.
• O logaritmo padrão em VS é 8
• O tamanho total da estrutura é: um múltiplo inteiro do número máximo de pares (o maior de todos os tipos de variáveis e o menor parâmetro de emparelhamento padrão).
• Se uma estrutura estiver aninhada, a estrutura aninhada se alinha a um número inteiro múltiplo de seu logaritmo máximo e ao tamanho geral da estrutura
É um múltiplo inteiro de todos os maiores logaritmos (incluindo logaritmos de estruturas aninhadas).
Isso é o que sabemos na linguagem C. Tomemos as seguintes questões como exemplo:
- // 计算⼀下A实例化的对象是多⼤?
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
- int _i;
- };
Não há função chamada, então basta olhar para as variáveis de membro. _ch é um tipo de caractere char, com tamanho de 1 byte. _i é um tipo int, com tamanho de 4 bytes. número de alinhamento, então é _ Coloque ch na posição com subscrito 0, deixe três espaços em branco e depois coloque _i Ocupa um total de 8 bytes de espaço de 0 a 7, que é um múltiplo inteiro do número máximo de alinhamento. . Atende às condições, então a resposta a esta pergunta é 8 .
E se forem essas duas perguntas?
- class B
- {
- public:
- void Print()
- {
- //...
- }
- };
-
- class C
- {
- };
Podemos descobrir que existe uma função vazia na classe B e nenhum membro na classe C. Na linguagem C, fazemos com que ambas as classes ocupem 1 byte de espaço.
• EExistem duas funções-membro, Init e Print, na classe ate. Não há distinção entre diferentes objetos no corpo da função. Então, quando d1 chama Init e Print.
Ao imprimir a função, como a função sabe se deve acessar o objeto d1 ou o objeto d2?Então aqui veremos que C++ dá
Um ponteiro this implícito resolve o problema aqui
• Após a compilação do compilador, as funções-membro da classe adicionarão um ponteiro do tipo de classe atual, chamado de ponteiro, à primeira posição do parâmetro formal por padrão. Por exemplo, o protótipo real da classe Init of Date é, void Init(Date* const this, int year,
int mês, int dia)
• Ao acessar variáveis-membro nas funções-membro de uma classe, elas são essencialmente acessadas através do ponteiro this. Por exemplo, ao atribuir um valor a _year na função Init, isto-.
>_ano = ano;
• C++ estipula que esse ponteiro não pode ser escrito explicitamente na posição dos parâmetros reais e dos parâmetros formais (o compilador tratará disso durante a compilação), mas esse ponteiro pode ser usado explicitamente no corpo da função.
por exemplo
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- _year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);this指针
- d1.Init(2024, 3, 31);
-
- // d1.Init(&d2, 2024, 7, 5);
- d2.Init(2024, 7, 5);
- //不能自己写
- return 0;
- }
Para a escrita dos parâmetros reais na função Init, C++ adicionará uma constante invisível a este ponteiro (que não pode ser modificada) à primeira variável da função, conforme mostrado nos comentários da função.
Contanto que o ponteiro nulo não seja desreferenciado, nenhum erro será relatado.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Embora p receba um ponteiro nulo e aponte para a função Print, ele não é desreferenciado, portanto, nenhum erro será relatado aqui.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- cout << _a << endl;//这个地方多了一句
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Comparado com o programa anterior, há apenas mais uma frase cout << _a << endl, mas neste momento o ponteiro nulo aponta para uma variável membro, e o ponteiro nulo será desreferenciado, então o programa reportará um erro.
A função de membro padrão é uma função de membro que não é explicitamente implementada pelo usuário e é gerada automaticamente pelo compilador. É chamada de função de membro padrão. Para uma classe, se não a escrevermos, o compilador irá gerar as seguintes 6 funções de membro padrão por padrão, que são as 6 seguintes
O construtor é uma função de membro especial. Deve-se notar que embora o nome do construtor seja chamado de construtor, a principal tarefa do construtor não é abrir espaço para criar objetos (o objeto local que frequentemente usamos é o espaço quando a pilha). frame é criado) ), em vez disso, o objeto é inicializado quando o objeto é instanciado. A essência é substituir a função Init de inicialização.
1. O nome da função é igual ao nome da classe.
2. Nenhum valor de retorno. (Não há necessidade de escrever vazio)
3. Quando o objeto for instanciado, o sistema chamará automaticamente o construtor correspondente.
4. Os construtores podem estar sobrecarregados.
5. Se não houver um construtor definido explicitamente na classe, o compilador C++ gerará automaticamente um construtor padrão sem parâmetros. Depois que o usuário o definir explicitamente, o compilador não o gerará mais.
Os primeiros cinco itens são relativamente simples, vamos dar um exemplo:
- class Date
- {
- public:
- //1.⽆参构造函数,无需写void
- Date()//若不写,则构造函数会写出这一中无参构造函数
- {
- _year = 1;
- _month = 1;
- _day = 1;
- }
- //2.全缺省构造函数
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- }
-
- int main()
- {
- Date d1;//此时相当于已经自动调用了Init函数
- Date d2;
-
- return 0;
- }
Dois construtores são escritos no programa, um é um construtor sem parâmetros e o outro é um construtor padrão completo. No entanto, embora não escrevamos uma função, o programa irá escrever até um construtor sem parâmetros. duas funções são funções sobrecarregadas, devido à ambiguidade de chamada, as duas funções não podem existir ao mesmo tempo.
6. Construtores sem parâmetros, construtores padrão completos e construtores gerados pelo compilador por padrão quando não escrevemos um construtor são todos chamados de construtores padrão. No entanto, uma e apenas uma destas três funções existe e não pode existir ao mesmo tempo. Embora o construtor sem parâmetros e o construtor padrão completo constituam uma sobrecarga de função, haverá ambiguidade ao chamá-los. Deve-se notar que muitos estudantes pensam que o construtor padrão é o construtor padrão gerado pelo compilador. Na verdade, o construtor sem parâmetros e o construtor padrão completo também são construtores padrão. Para resumir, eles são construtores que podem ser chamados sem passar pelo real. parâmetros. É chamada de construção padrão.
O sexto recurso é mais complicado: o construtor sem parâmetros, o construtor padrão completo e o construtor gerado pelo compilador por padrão quando não escrevemos um construtor são todos chamados de construtores padrão e só pode haver um. construtor padrão como o construtor gerado pelo compilador por padrão!
7. Se não o escrevermos, o construtor gerado pelo compilador por padrão não possui requisitos para a inicialização de variáveis de membro do tipo interno. Em outras palavras, se ele é inicializado ou não, é incerto, depende do compilador. . Para variáveis de membro de tipo personalizado, é necessário chamar o construtor padrão desta variável de membro para inicializá-la. Se esta variável de membro não tiver um construtor padrão, um erro será relatado. Se quisermos inicializar esta variável de membro, precisaremos usar uma lista de inicialização para resolver o problema. Explicaremos a lista de inicialização em detalhes no próximo capítulo. .
A função do destruidor é oposta à do construtor. O destruidor não completa a destruição do próprio objeto. Por exemplo, o objeto local possui um quadro de pilha. Quando a função termina e o quadro de pilha é destruído. liberado. Não precisamos controlá-lo. C++ estipula que os objetos Quando destruídos, o destruidor será automaticamente chamado para completar a limpeza e liberação de recursos no objeto. A função do destruidor é semelhante à função Destroy que implementamos antes no Stack. Por exemplo, Date não possui Destroy. Na verdade, não há recursos a serem liberados. destruidor é exatamente como a função Destroy. Ele deve ser usado para estruturas de dados que requerem objetos não locais, como pilhas e filas, para solicitar recursos.
1. O nome do destruidor é precedido pelo caractere ~ antes do nome da classe. (Semelhante à negação bit a bit na linguagem C)
2. Sem parâmetros e sem valor de retorno. (Isso é semelhante à estrutura e não há necessidade de adicionar vazio)
3. Uma classe só pode ter um destruidor. Se não for definido explicitamente, o sistema gerará automaticamente um destruidor padrão.
4. Quando o ciclo de vida do objeto terminar, o sistema chamará automaticamente o destruidor.
5. Semelhante ao construtor, se não escrevermos um destruidor gerado automaticamente pelo compilador, ele não processará os membros do tipo integrado e chamará seus destruidores.
6. Deve-se notar também que quando escrevemos explicitamente o destruidor, o destruidor do membro do tipo personalizado também será chamado. Ou seja, o destruidor do membro do tipo personalizado será chamado automaticamente, independentemente da situação.
7. Se não houver recursos solicitados na classe, o destruidor não precisa ser escrito e o destruidor padrão gerado pelo compilador pode ser usado diretamente, como Date, se o destruidor gerado padrão puder ser usado, existe; não há necessidade de exibi-lo. Escreva o destruidor, como MyQueue, mas quando houver um aplicativo de recurso, você mesmo deve escrever o destruidor, caso contrário, causará vazamento de recursos, como Stack.
8. Para múltiplos objetos em um domínio local, C++ estipula que aqueles definidos posteriormente serão destruídos primeiro.
Assim como o construtor acima, o destruidor padrão gerado automaticamente pelo C++ geralmente não será de grande ajuda para nós, mas é mais conveniente em alguns casos raros, como usar duas pilhas para implementar uma fila:
- public:
- Stack(int n = 4)//相当于Init
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc fail!");
- return;
- }
- _capacity = n;
- _top = 0;
- }
- ~Stack()//相当于Destroy
- {
- cout << "~Stack()" << endl;
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
-
- class MyQueue
- {
- public:
- //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
- // 显⽰写析构,也会⾃动调⽤Stack的析构
-
- private:
- Stack pushst;
- Stack popst;
- };
Neste momento, o compilador em MyQueue chama automaticamente o construtor (inicialização) e o destruidor (destruição) da pilha.