1. layout de memória C ++ é dividido em várias zonas, cada uma com características?
Em C ++, um programa armazenado na memória é dividida em cinco áreas:
1), a área de pilha (Stack): atribuído liberados automaticamente pelo compilador, os parâmetros da função de armazenamento, as variáveis locais, e similares. Operar de forma semelhante a uma estrutura de dados pilha.
2), a área da pilha (pilha): geralmente libertadas atribuído pelo programador, se o programador não liberta no final do programa podem ser recuperados pelo sistema operacional. Note que é uma estrutura de dados pilha totalmente diferente, que toca lista de distribuição similar.
3), zona global / estática (estático): variáveis globais e variáveis estáticas são armazenados em uma do programa compilado particionado
4), literais área: armazenar cadeias constantes
5), a área de código de programa de: armazenar o corpo da função (função de membro da classe, função global) código binário
2. Quando você define uma classe, o compilador irá gerar automaticamente que funciona como uma classe? Quais são as características de cada uma destas funções?
Para uma classe vazia, C ++ compilador gera a função de quatro padrão membro: construtor padrão, destructor, cópia (cópia) construtor, função de atribuição.
1), um construtor padrão (construtor padrão):
construtor chama quando nenhum inicializador explícito. Ele fornece um construtor argumento parâmetro padrão é definido como todos formados a partir do construtor sem argumentos, ou. Se nenhum inicializador definir uma variável de uma classe usa o construtor padrão. Se uma classe definida pelo utilizador não definir explicitamente qualquer construtor, o compilador gera automaticamente um construtor predefinido para esse tipo, conhecido como construtor sintico (construtor padrão sintetizado). O construtor da classe para fornecer a linguagem C ++ pode automatizar o objeto de tarefa de inicialização. Construtores objetos globais e objetos estáticos na função main () é invocado antes da execução, os objetos estáticos locais do construtor são executadas quando o primeiro programa a ser chamado quando a instrução apropriada. No entanto, dada a referência a um objeto externo quando a instrução não chama o construtor apropriado, porque o objeto externo somente é referenciado objetos em outros lugares declarados, e realmente não criar um objeto.
2), o processo de destruição:
Quando o programador não cria um destruidor de classe, em seguida, o sistema cria automaticamente uma classe destructor, forma: ~ A () {}, o processo de destruição para a classe é criada A. Quando o programa estiver concluído, o sistema chama automaticamente o destruidor é criado automaticamente, um objeto é liberado. O novo operador destrutor default não pode objetos de exclusão alocados na memória ou objeto membro livremente. Se os membros da classe espaço ocupado é alocada dinamicamente no construtor, devemos personalizar o destruidor então explicitamente usando o operador delete para liberar o construtor usa a nova memória operador alocado, assim como a destruição variáveis comuns.
3), o construtor de cópia:
Se a gravação não é funções construtor de cópia e de cessão de ativos, o compilador irá "copiar pedaços" função padrão gerado automaticamente maneira. Se a classe contém uma variável ponteiro, em seguida, a função de dois padrão implicitamente errado.
String tem dois objetos em a, b, por exemplo, suponha conteúdo a.m_data para "Olá", o conteúdo do b.m_data "mundo".
Vai agora ser atribuído a um b, função de atribuição padrão dos meios "cópia bit" Executando b.m_data = a.m_data. Isso fará com que três erros:
A, memória original b.m_data não é liberada, causando vazamento de memória;
b, b.m_data e ponto a.m_data para o mesmo pedaço de memória, a ou b quaisquer mudanças afetarão a outra parte;
c , quando o objecto é destruído, m_Data foi libertada duas vezes.
construtor de cópia é chamado:
Uma Quando um objeto existente é para ser atribuído a outro novo objeto, ele chama o construtor de cópia.
b. Quando argumentos e parâmetros são objeto envolvido na ligação do parâmetro real, as chamadas cópia do construtor.
c. Quando o valor de retorno é uma função do objeto, para completar a chamada de função retorna, ele chama o construtor de cópia.
4) função de atribuição:
Cada classe tem apenas uma função de atribuição. Cópia construtor e função de atribuição é muito confuso, muitas vezes leva a escrever errado, uso errado. construtor de cópia é chamado quando o objeto é criado, e a função de avaliação só pode ser chamado tem sido em torno de objetos.
3. O que é cópia superficial, o que é cópia profunda?
Cópia superficial é a adição de um ponteiro para a memória já existente. A cópia profunda é a adição de um ponteiro, e abriu-se um novo espaço para um ponteiro para este novo espaço abriu. Rasa copiar vários objetos que apontam para um espaço de tempo, um espaço levará à liberação de outros objetos espaciais usados também foram liberados, será lançado novamente erros.
- Implementar uma classe String personalizado, para garantir a implementação da função principal corretamente.
class String
{
public:
String();
String(const char *pstr);
String(const String & rhs);
String & operator=(const String & rhs);
~String();
void print();
private:
char * _pstr;
};
int main(void)
{
String str1;
str1.print();
String str2 = "Hello,world";
String str3("wangdao");
str2.print();
str3.print();
String str4 = str3;
str4.print();
str4 = str2;
str4.print();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include "string.h"
#include <iostream>
//#include "stdio.h"
using std::cout;
using std::endl;
//using std::string;
//倾向于将std::string当成一个内置类型使用
class Point
{
public:
//当没有定义默认构造函数时,编译器会主动提供一个构造函数的作用就是用来初始化数据成员
//一旦定义了一个有参构造函数时,编译器就不会再主动提供默认构造函数
//构造函数在执行时,先执行初始化表达式,再执行构造函数执行体,
// 如果没有在构造函数的初始化列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。
Point()
:_ix(0),_iy(0)/*,_iz(0)//合法*/
{
//_iz = 0;//违法
cout << "Point()//默认构造函数" << endl;
}
Point(int x,int y)
:_ix(x), _iy(y)
{
cout << "Point(x,y)//构造函数" << endl;
}
//每个成员在初始化列表之中只能出现一次,其初始化的顺序不是由成员变量在初始化列表中的顺序决定的,
// 而是由成员变量在类中被声明时的顺序决定的。
Point(int val)
:_iy(val),
_ix(_iy)
{
cout << "Point()//初始化顺序" << endl;
}
//拷贝构造函数
Point(const Point &rhs)
:_ix(rhs._ix),
_iy(rhs._iy)
{
cout << "Point(const Point &rhs)//拷贝构造函数" << endl;
}
//析构函数:清理工作
~Point()
{
cout << "~Point()//析构函数" << endl;
}
//在类中定义的非静态成员函数中都有一个隐含的this指针,它代表的就是当前对象本身,
// 它作为成员函数的第一个参数,由编译器自动补全。
// 比如print 函数的完整实现是:
void print(/*Point * const this*/) //这里的const又是为什么
{
cout << "(" << _ix << "," << _iy << ")" << endl;
//cout << "(" << this->_ix << "," <<this->_iy << ")" << endl; //为什么编译不了?
}
private:
int _ix;
int _iy;
//const int _iz;
};
class Line
{
//在C++ 的类中,有4种比较特殊的数据成员,他们分别是常量成员、引用成员、
//类对象成员和静态成员,他们的初始化与普通数据成员有所不同。
public:
Line(int x1, int y1, int x2, int y2)
:_pt1(x1, y1),
_pt2(x2, y2)
{
cout<<"Line(int, int, int, int)//类对象成员初始化" << endl;
}
void print()
{
_pt1.print();
cout << "---->" << endl;
_pt2.print();
}
private:
Point _pt1;
Point _pt2;
};
class String
{
public:
String()//默认构造函数
{
_pstr=new char[10]();
strcpy(_pstr, "null");
cout << "默认构造函数" << endl;
}
String(const char *pstr)//构造函数
:_pstr ( new char[strlen(pstr) + 1]())
{
strcpy(_pstr, pstr);
cout << "构造函数" << endl;
}
String(const String & rhs)//拷贝构造函数
:_pstr(new char[strlen(rhs._pstr) + 1]())
{
strcpy(_pstr, rhs._pstr);
cout << "拷贝构造函数" << endl;
}
String & operator=(const String & rhs)//赋值运算符函数
{
if (this != &rhs)//排除自复制
{
//回收原来的堆空间
delete[] _pstr;
_pstr = new char[strlen(rhs._pstr) + 1]();
strcpy(_pstr, rhs._pstr);//深拷贝
}
cout << "赋值运算符函数" << endl;
return *this;
}
~String()//析构函数
{
delete[] _pstr;
cout << "析构函数" << endl;
}
void print()
{
cout << "str:" << _pstr << endl;
}
private:
char * _pstr;
};
class Computer
{
public:
//这种只拷贝指针的地址的方式,我们称为浅拷贝。当两个对象被销毁时,就会造成double free 的问题。
Computer(char * brand, double price)
:_brand(brand), //浅拷贝
_price(price)
{
cout << "Computer()//构造函数 浅拷贝" << endl;
}
Computer(const char * brand, double price) //这里为什么要用const?
:_brand ( new char[strlen(brand) + 1]()), //深拷贝
_price(price)
{
strcpy(_brand, brand);
setTotalPrice();
cout << "Computer()//构造函数 深拷贝" << endl;
}
//系统自动提供一个拷贝构造函数, 固定写法
//1. 引用符号不能去掉,如果去掉,根据拷贝构造函数的调用时机来看,会导致无穷递归,直到栈溢出,程序崩溃
//2. const关键字不能去掉,如果去掉,当传递过来的是右值时,就无法正确调用拷贝构造函数
Computer(const Computer &rhs)
:_price(rhs._price),
_brand(new char[strlen(rhs._brand) + 1]())
{
strcpy(_brand, rhs._brand);
cout << "Computer(const Computer &rhs)//拷贝构造函数" << endl;
setTotalPrice();
}
Computer & operator=(const Computer &rhs)
{
//1.避免自复制
if (this != &rhs)
{
//2.回收原来的堆空间
delete[] _brand;
//3.深拷贝
_brand = new char[strlen(rhs._brand) + 1]();
strcpy(_brand, rhs._brand);
cout << "Computer & operator=(const Computer &rhs)//赋值运算符函数" << endl;
}
setTotalPrice();
}
void setTotalPrice()
{
_totalPrice += _price;
}
//由于数据成员_brand指向了堆空间的区域,所以必须要显式提供一个析构函数进行回收
//析构函数要清理的是对象的数据成员申请的资源,而对象本身所占据的空间,不是由析构函数来回收的
//只要对象被销毁,就会自动调用析构函数
//不建议显示调用析构函数
//只有delete表达式才能回收对象占据的空间
~Computer()
{
delete[] _brand;
cout << "~Computer()" << endl;
}
void print()
{
cout << "The brand is:" << _brand <<"."<< endl;
cout << "The price is:" << _price << "." << endl;
cout << "The totalprice is:" << _totalPrice << "." << endl;
}
private:
char *_brand;
double _price;
//C++ 允许使用static (静态存储)修饰数据成员,这样的成员在编译时就被创建并
// 初始化的(与之相比,对象是在运行时被创建的),且其实例只有一个,被所有该
// 类的对象共享,就像住在同一宿舍里的同学共享一个房间号一样。静态数据成员和
// 之前介绍的静态变量一样,当程序执行时,该成员已经存在,一直到程序结束,任
// 何该类对象都可对其进行访问,静态数据成员存储在全局 / 静态区,并不占据对象的
// 存储空间。
//一般来说,我们不能在类的内部初始化静态数据成员,必须在类的外部定义和初始化静态数据成员,
//且不再包含static 关键字,
static double _totalPrice;
};
//初始化静态数据成员
//类型 类名::变量名 = 初始化表达式; //普通变量
//类型 类名::对象名(构造参数); //对象变量
double Computer::_totalPrice = 0;
int testPoint(void)
{
Point a;
a.print();
Point b(1, 2);
b.print();
Point c(3);
c.print();
Point d = b;
d.print();
return 0;
}
int testLine(void)
{
Line line(1, 2, 3, 4);
line.print();
return 0;
}
int testString(void)
{
String str1;
str1.print();
String str2 = "Hello,world";
str2.print();
String str3("wangdao");
str3.print();
String str4 = str3; //拷贝构造函数
str4.print();
str4 = str2; //赋值运算符函数
str4.print();
//system("pause");
return 0;
}
int testComputer(void)
{
//char brand[] = "Alien";
//Computer com1(brand, 20000);
//com1.print();
//strcpy(brand, "change");
//com1.print();
//栈对象
const char brand1[] = "Alien";
Computer com2(brand1, 20000);
com2.print();
//堆对象
Computer* com3=new Computer("Macbook pro", 20000);
com3->print();
//main函数退出之后,静态对象会自动被销毁
static Computer com4("Xiaomi", 7777);
com4.print();
Computer com5 = com4;
com5.print();
cout << endl;
cout << "testComputer() over..." << endl;
return 0; //在这里打断点只有一个析构函数,为什么在return的时候出错误了?
}
//main函数退出之后,全局对象会自动被销毁
//Computer com6("Thinkpad", 8888);
int main(void)
{
//testPoint();
//testLine();
//testString();
testComputer();
//cout << endl;
//cout << "return to main()..." << endl;
//cout << endl;
//com6.print(); //退出为什么只有一个析构函数?
system("pause");
return 0;
}
membros de dados de inicialização especiais de
dados constantes membros const, citou um membro dos membros do objeto classe, membros estáticos
1. O único membro constantelista inicializador construtorInicializado
2. Em referência a membros somentelista inicializador construtorInicializar
3. Os membros do objeto de classe somente nolista inicializador construtorInicializar
4. inicialização membro estático