C++:动态内存分配和释放、类型转换、面向对象编程、构造函数
2017-02-02 21:08
513 查看
一、C++动态内存分配/释放
1. C++内存分配运算符: new / delete
1)分配 / 释放单个对象的方法:
new / delete
int *pi = new int; //返回分配的内存的首地址,int *类型
...
delete pi; //把pi指向的内存区域释放掉
pi = NULL;
2)动态分配单个对象并初始化的方法
int *pi = new int(初始值); //int *pi = new int(100);
... // *pi 的结果就是 100;
delete pi;
pi = NULL;
2. 分配 / 释放对象数组的方法: new[] / delete[]
int * pi;
pi = new int[10]; //new[] 为运算符。 int *pi = new[对象个数];
...
delete[] pi; //释放对象数组,不需要写数组个数
pi = NULL;
说明:在C++98 标准中,动态分配对象数组时不能把数组元素逐个初始化。
3. 动态内存的错误处理:
C++中用异常机制来实现 new/delete 错误处理。
如果内存分配失败,则会抛出"std::bad_alloc"异常。
4. 注意事项:
1)重复释放一个对象会出现异常,为避免此异常,通常在释放对象后,将指针置空,然后再次调用 "delete 指针;" 释放的时候将被忽略。
二、C++类型转换
C语言中的隐式类型转换:
int i = 300;
char c = i; //隐式类型转换
//i -->> (char型临时对象) -->> c
C语言中的显式类型转换:
char ch = (char) i; //显式类型转换(强制类型转换)
C++中的显式类型转换方法:
char ch1 = char(i); //用构造函数来进行类型转换
C++类型(cast)转换:
1. 静态类型转换 static_cast (能够在C里隐式类型转换的都可用此转)
作用:尽可能保证原值不变的情况下,将类型转换为目的类型
1)语法规则:
static_cast<目的类型>(源类型的变量或表达式);
2)与标准C中强制类型转换的区别:
当编译器"不能隐式类型转换时,编译直接报错"。
double pi = 3.14; //IEEE浮点数标准 .785*2的平方
int *p = (int *)π //强转,取了double低4位的字节,含小数位
cout << *p << endl; //一个确定的很长的负数
int *pp;
pp = static_cast<int*>(&pi); //编译直接报错
2. 去常类型转换 const_cast
作用:去掉一个对象的常量属性(const)
a. 可以用于"去除指针的常属性";
char name[] = "tarena";
//以下示意指针去常
const char * p = name;
char * p2;
// p2 = p; //p拥有常属性,编译此行报错
p2 = const_cast<char*>(p); //指针去掉常属性
*p2 = 'T';
cout << name << endl;//Tarena
b. 可以"去除引用的常属性"。
void printInt(const int & ri) {
cout << ri << endl;
int & r = const_cast<int&>(ri);
r = 200; //引用去常属性后合法
cout << r << endl;//200
}
1)语法规则:
const_cast<目的类型>(源对象);
3. 动态类型转换 dynamic_cast
4. 重解释类型转换 reinterpret_cast
作用:对于以上三种 cast 都不能转换的类型进行强制类型转换。等同于C语言的强制类型转换。
1)语法规则:
reinterpret_cast<目的类型>(源对象)
"cv 属性"(c:const
v:volatile)
const 属性:常量
volatile 属性(嵌入式必用):挥发
三、面向对象编程
1. 面向对象的特点:"封装、继承/派生、多态"
1)封装的作用
保护属性的合法操作,拒绝非法操作。"对象:生活当中的实例"
2. 对象和类:
关系:类是对象的描述,对象是类的实例。
3. 类的声明语法规则:
struct/class 类名(标识符) [: 继承列表] {
[访问权限限定符 : ]
构造函数
析构函数
其他成员函数
其他成员对象
};
4. 创建对象
类名 对象名;
Human h1;
5. 访问权限限定符(3 种):
公有:public "公有权限,破坏对象的封装性。通常做【接口】使用"
保护:protected
私有:private "通常用来保存 数据"
1) public 和 private 的区别:
public 成员(对象/函数)任何函数和类都可以访问。
private 成员(对象/函数)只能被当前类的成员函数访问。
6. class 和 struct 的区别:
class 默认所有成员都为私有(private)访问权限。
struct 默认所有成员都为公有(public)访问权限。
四、构造函数
1. 作用:
用来初始化对象内的成员;
2. 构造函数的语法格式:
类名(形参表) :初始化列表 {
...
}
3. 说明:
1)如果没有定义任何构造函数,则编译器会为该类添加一个无参的构造函数。通常这个构造函数什么都不做。
2)构造"函数名必须为类名"。
3)构造函数在"创建对象时会被自动调用"。
4)构造函数"可以重载",在调用的时候根据实参类型和个数进行匹配。
5)构造函数"可以设置默认实参"。
6)如果定义了任何构造函数,则编译器不会为添加无参的构造函数。
4. 构造函数的调用方法
1)类型 对象;
Human h1; //默认以没有参数的形势调用
2)类型 对象(实参);
Human h2("张飞", 25);
3)类型 对象 = 构造函数名(实参);
Human h3 = Human("张飞", 28);
五、构造函数初始化列表
1. 对象构造(创建)的过程
1)自上而下依次创建对象内的成员
2)创建对象自身
3)调用构造函数
2. 作用:
显式的调用类内成员的构造函数来初始化成员对象。
说明:
1)引用一定要在初始化列表里初始化;
2)有常属性的对象也一定要在初始化列表里初始化。
类的常量型和引用型成员变量,必须初始化表中显式初始化,不能在构造函数中赋初值
作业:
封装一个dog类;
属性:品种,年龄,体重,颜色
行为:进食,睡眠,玩耍
$: a.out
请输入小狗的属性和行为:
品种:泰迪犬
年龄:3
重量:0.8
颜色:棕色
行为:日空气
一只3岁0.8千克棕色的泰迪犬,正在日空气...
1. C++内存分配运算符: new / delete
1)分配 / 释放单个对象的方法:
new / delete
int *pi = new int; //返回分配的内存的首地址,int *类型
...
delete pi; //把pi指向的内存区域释放掉
pi = NULL;
2)动态分配单个对象并初始化的方法
int *pi = new int(初始值); //int *pi = new int(100);
... // *pi 的结果就是 100;
delete pi;
pi = NULL;
2. 分配 / 释放对象数组的方法: new[] / delete[]
int * pi;
pi = new int[10]; //new[] 为运算符。 int *pi = new[对象个数];
...
delete[] pi; //释放对象数组,不需要写数组个数
pi = NULL;
说明:在C++98 标准中,动态分配对象数组时不能把数组元素逐个初始化。
3. 动态内存的错误处理:
C++中用异常机制来实现 new/delete 错误处理。
如果内存分配失败,则会抛出"std::bad_alloc"异常。
/** 代码演示 **/ #include <iostream> using namespace std; int main(void) { int * pi; try { pi = new int[0xFFFFFFFF]; //分配16G的内存 cout << "hello" << endl; //此处不会被执行,跳catch语句执行 delete[] pi; //正常分配内存时,try执行到最后释放。 } catch(exception & ex) { cout << "内存分配失败了\n"; cout << ex.what() << endl; // std::bad_alloc } cout << "此处做其他的事情..." << endl; cout << pi << " " << *pi << endl; //一个地址和一个很大的数字 return 0; }
4. 注意事项:
1)重复释放一个对象会出现异常,为避免此异常,通常在释放对象后,将指针置空,然后再次调用 "delete 指针;" 释放的时候将被忽略。
/** 代码演示 **/ #include <iostream> #include <stdlib.h> using namespace std; int main(void) { int * pi; pi = new int(100); cout << "C++ pi = " << *pi << endl; delete pi; // delete pi; //此时会出错:段错误!!delete第2次 pi = NULL; //指针置空,增强程序健壮性(不会崩溃),好习惯。 delete pi; //此时不会出错,程序会忽略掉指针置空后的释放动作 return 0; }
二、C++类型转换
C语言中的隐式类型转换:
int i = 300;
char c = i; //隐式类型转换
//i -->> (char型临时对象) -->> c
C语言中的显式类型转换:
char ch = (char) i; //显式类型转换(强制类型转换)
C++中的显式类型转换方法:
char ch1 = char(i); //用构造函数来进行类型转换
C++类型(cast)转换:
1. 静态类型转换 static_cast (能够在C里隐式类型转换的都可用此转)
作用:尽可能保证原值不变的情况下,将类型转换为目的类型
1)语法规则:
static_cast<目的类型>(源类型的变量或表达式);
2)与标准C中强制类型转换的区别:
当编译器"不能隐式类型转换时,编译直接报错"。
double pi = 3.14; //IEEE浮点数标准 .785*2的平方
int *p = (int *)π //强转,取了double低4位的字节,含小数位
cout << *p << endl; //一个确定的很长的负数
int *pp;
pp = static_cast<int*>(&pi); //编译直接报错
2. 去常类型转换 const_cast
作用:去掉一个对象的常量属性(const)
a. 可以用于"去除指针的常属性";
char name[] = "tarena";
//以下示意指针去常
const char * p = name;
char * p2;
// p2 = p; //p拥有常属性,编译此行报错
p2 = const_cast<char*>(p); //指针去掉常属性
*p2 = 'T';
cout << name << endl;//Tarena
b. 可以"去除引用的常属性"。
void printInt(const int & ri) {
cout << ri << endl;
int & r = const_cast<int&>(ri);
r = 200; //引用去常属性后合法
cout << r << endl;//200
}
1)语法规则:
const_cast<目的类型>(源对象);
3. 动态类型转换 dynamic_cast
4. 重解释类型转换 reinterpret_cast
作用:对于以上三种 cast 都不能转换的类型进行强制类型转换。等同于C语言的强制类型转换。
1)语法规则:
reinterpret_cast<目的类型>(源对象)
"cv 属性"(c:const
v:volatile)
const 属性:常量
volatile 属性(嵌入式必用):挥发
/** 代码演示:reinterpret_cast **/ #include <iostream> #include <string.h> using namespace std; struct http_head { char http[5]; char major; char dot; char minor; }; int main(void) { double pi = 3.1415; int *p; p = reinterpret_cast<int*>(&pi); cout << *p << endl;//-1065151889 char buffer[1024]; strcpy(buffer, "http 1.2\r\ncontent_len:30\r\n"); http_head *hh = reinterpret_cast<http_head *>(buffer); cout << "主版本号:" << hh->major << endl; //主版本号:1 cout << "次版本号:" << hh->minor << endl; //次版本号:2 return 0; }
/** 代码演示:volatile 属性 **/ #include <iostream> using namespace std; int main(void) { volatile int i = 10;//volatile属性告诉编译器不要挥发掉i变量 const volatile int j = i;//j不被优化掉,且j不可变常属性 int k = j; cout << k << endl; //以上4句等同于此1句话,编译器优化掉了i,j,k cout << 10 << endl; return 0; }
三、面向对象编程
1. 面向对象的特点:"封装、继承/派生、多态"
1)封装的作用
保护属性的合法操作,拒绝非法操作。"对象:生活当中的实例"
2. 对象和类:
关系:类是对象的描述,对象是类的实例。
3. 类的声明语法规则:
struct/class 类名(标识符) [: 继承列表] {
[访问权限限定符 : ]
构造函数
析构函数
其他成员函数
其他成员对象
};
4. 创建对象
类名 对象名;
Human h1;
5. 访问权限限定符(3 种):
公有:public "公有权限,破坏对象的封装性。通常做【接口】使用"
保护:protected
私有:private "通常用来保存 数据"
1) public 和 private 的区别:
public 成员(对象/函数)任何函数和类都可以访问。
private 成员(对象/函数)只能被当前类的成员函数访问。
6. class 和 struct 的区别:
class 默认所有成员都为私有(private)访问权限。
struct 默认所有成员都为公有(public)访问权限。
/** 代码演示 **/ #include <iostream> using namespace std; //struct Human { class Human { private: //私有权限 string name; int age; long long int id; //身份证号 public: //公有权限 void setName(const string & n) { name = n; } void eat(string what){ cout << name << "正在吃" << what << "..." << endl; } void sleep(int hour) { cout << name << "睡了" << hour << "个小时" << endl; } }; int main(void) { Human h1/* = {"张飞", 25, 10086}*/; h1.setName("张飞"); h1.eat("包子"); h1.sleep(8); Human h2/* = {"赵云", 28, 10000}*/; h2.setName("赵云"); h2.eat("馒头"); h2.sleep(10); // h1.name = "关羽"; //破坏私有权限,编译报错!! return 0; }
四、构造函数
1. 作用:
用来初始化对象内的成员;
2. 构造函数的语法格式:
类名(形参表) :初始化列表 {
...
}
3. 说明:
1)如果没有定义任何构造函数,则编译器会为该类添加一个无参的构造函数。通常这个构造函数什么都不做。
2)构造"函数名必须为类名"。
3)构造函数在"创建对象时会被自动调用"。
4)构造函数"可以重载",在调用的时候根据实参类型和个数进行匹配。
5)构造函数"可以设置默认实参"。
6)如果定义了任何构造函数,则编译器不会为添加无参的构造函数。
4. 构造函数的调用方法
1)类型 对象;
Human h1; //默认以没有参数的形势调用
2)类型 对象(实参);
Human h2("张飞", 25);
3)类型 对象 = 构造函数名(实参);
Human h3 = Human("张飞", 28);
/** 代码演示 constructor.cpp **/ #include <iostream> using namespace std; class Human { string name; int age; public: Human() { name = "没名"; age = 1; cout << "我是构造函数!" << endl; } Human(const string & n, int a = 1) { name = n; age = a; cout << "我是构造函数 - 重载!" << endl; } void printInfo() { cout << "我叫" << name << ",我今年" << age << "岁。\n"; } }; int main(void) { Human h1; h1.printInfo(); Human h2("张飞", 25); h2.printInfo(); Human h3 = Human("赵云", 28); //= 初始化运算符 h3.printInfo(); Human h4("刘备"); h4.printInfo(); return 0; }
五、构造函数初始化列表
1. 对象构造(创建)的过程
1)自上而下依次创建对象内的成员
2)创建对象自身
3)调用构造函数
2. 作用:
显式的调用类内成员的构造函数来初始化成员对象。
说明:
1)引用一定要在初始化列表里初始化;
2)有常属性的对象也一定要在初始化列表里初始化。
类的常量型和引用型成员变量,必须初始化表中显式初始化,不能在构造函数中赋初值
/** 构造函数代码演示 **/ #include <iostream> using namespace std; class A { public: A() { cout << "A()\n"; } }; class Math { public: Math(double d) : pi(d), i(0) { //成员函数都在TEXT段 cout << "Math()" << pi << endl; //pi = d; } private: A a; const double pi; // double pi; int i; }; int main(void) { Math m(3.14); //m的构造过程 /* 1> m.pi; 2> m.i; 3> m; 4> m的构造函数 */ cout << sizeof(m) << endl; //12 cout << sizeof(long double) << endl; //64bit 16 cout << sizeof(long double) << endl; //32bit 12 return 0; }
/** 初始化列表代码演示 **/ #include <iostream> using namespace std; class Math { public: Math(int & ri) : m_i(ri) { // m_i <=> ri cout << "Math() " << m_i << endl; m_i = 200; //把main函数的i值改了 } void setI(int j) { m_i = j; } private: int & m_i; }; int main(void) { int i = 100; Math m(i); cout << "i = " << i << endl; m.setI(300); cout << "i = " << i << endl; return 0; }
作业:
封装一个dog类;
属性:品种,年龄,体重,颜色
行为:进食,睡眠,玩耍
/** 作业完成代码 **/ #include <iostream> #include <string> using namespace std; class Dog { public: Dog(): breed("土狗"), age(1), weight(1.00), color("黄色") { } void setInfo(string const & b, int const & a, double const & w, string const & c) { breed = b; age = a; weight = w; color = c; } void behavior(string & what) { cout << "正在" << what << "..." << endl; } void printInfo() { cout << "一只" << age << "岁" << weight << "千克" << color << "的" << breed << ","; } private: string breed; int age; double weight; string color; }; int main(void) { Dog dog; cout << "请输入小狗的属性和行为:" << endl; string breed; cout << "品种:"; cin >> breed; int age; cout << "年龄:"; cin >> age; double weight; cout << "重量:"; cin >> weight; string color; cout << "颜色:"; cin >> color; string behav; cout << "行为:"; cin >> behav; dog.setInfo(breed, age, weight, color); dog.printInfo(); dog.behavior(behav); return 0; }
$: a.out
请输入小狗的属性和行为:
品种:泰迪犬
年龄:3
重量:0.8
颜色:棕色
行为:日空气
一只3岁0.8千克棕色的泰迪犬,正在日空气...
相关文章推荐
- c++ 使用动态内存分配的类需要显式复制构造函数,赋值构造函数,析构函数
- C和C++如何动态分配和释放内存,他们的区别是什么
- C与C++动态分配,释放内存的区别
- c与c++分别是怎样动态分配和释放内存的,有什么区别?
- C++动态二维数组内存的分配和释放 opencv
- [笔试] C和C++动态内存分配和释放的区别
- C和C++动态内存分配和释放的区别
- c与c++分别是怎样动态分配和释放内存的,有什么区别?
- C和C++动态内存分配和释放的区别
- 【小记备忘】之C/C++ 杂记,catch,调用构造函数,内存分配,隐式转换【2013.12.11】
- C与C++动态分配,释放内存的区别
- C与C++动态分配,释放内存的区别
- C/C++动态分配与释放内存的区别详细解析
- C和C++动态内存分配和释放的区别
- c与c++分别是怎样动态分配和释放内存的,有什么区别?
- C和C++动态内存分配和释放的区别
- C与C++动态分配,释放内存的区别
- C与C++动态分配,释放内存的区别
- C与C++中动态分配与释放内存的区别
- 【问题】c/c++函数内部动态分配的内存,函数执行完毕会释放吗?