C++:动态内存分配和释放、类型转换、面向对象编程、构造函数

来源:互联网 发布:剑灵萝莉捏脸数据 编辑:程序博客网 时间:2024/05/16 00:30
一、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 *)&pi; //强转,取了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:constv: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千克棕色的泰迪犬,正在日空气...


0 0
原创粉丝点击