您的位置:首页 > 编程语言 > C语言/C++

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"异常。
/** 代码演示 **/
#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++