对象创建、this指针、构造函数、析构函数、拷贝构造函数

2.3 对象创建

3. 对象创建
        对象本质上就是⼀块内存这块内存中存放的是创建这个对象的类描述的数据
⼀、在栈上创建对象
在函数内部,⽤类定义⼀个变量, 这个变量就是栈上创建的对象
int main(void)
{
    Student stu;
    return 0;
}
在函数调⽤结束的时候栈上分配的内存会⾃动释放掉, 这个对象也就销毁掉了
⼆、在静态区创建
  • ⽤类在函数外⾯定义⼀个全局变量
  • ⽤ static 修饰的局部变量
        在程序结束的时候静态区的内存空间会⾃动释放掉
三、在堆区创建
  • 通过 new 运算符分配内存
  • 通过 delete 运算符释放内存
Student *pstu = new Student;
delete pstu;

四、任务
        ⽤时间类在栈区、静态区、堆区创建对象

2.4 this指针

4.this指针
一、由来
        编译器默认会给类的非静态成员函数添加一个this指针,这个指针指向调用这个成员函数对象的首地址。
二、总结
  • this指针是一个特殊的指针,指向类对象自身的首地址
  • 每个类对象的非静态****成员函数都有一个this指针,指向调用成员函数对象的首地址
  • 如果在成员函数中需要引用整个对象,则使用*this
  • 当成员函数的参数和成员变量同名时候,可以通过this显示区分
成员函数的参数和成员变量同名情况分析:
void Student::setName(const string &name)
{ 
 //这⾥实际上是参数name在进⾏⾃⼰给⾃⼰赋值 
     name = name;
}
void Student::setName(const string &name)
{
    this->name = name;
}

 

2.5 构造函数

5. 构造函数
        构造函数是⼀个特殊的成员函数名字和类名相同没有返回值,创建类类型对象时,⾃动调⽤在对象的⽣命周期内只能调⽤⼀次保证每个数据成员都有⼀个合适的初始值。
下⾯代码中哪些是构造函数?
class Student{
public:
    void Student();
    void Student(const string &name,int age,int score);
    Student();
    Student(const string &name,int age,int score);
    void playGame(const string &game);
    void setName(const string &value);
    void setAge(int value);
    void setScore(int value);
private:
    string name;
    int age;
    int score;
};
Student::Student(const string &name,int age,int score)
{
    this->name = name;
    this->age = age;
    this->score = score;
}
    Student::Student(const string &name,int age,int score):
    name(name),
    age(age),
    score(score)
{

}
  • 函数名和类名⼀样
  • 没有返回值
  • 创建对象的时候,⾃动调⽤
  • 如果在设计⼀个类的时候,没有⼿动添加构造函数,编译器会根据需要⾃动添加添加⼀个默认 的⽆参构造函数
  • 如果⾃⼰添加了构造函数,编译器就不会再添加默认构造函数
  • 构造函数不能⽤ const 修饰
  • 构造函数不能是虚函数
设计⼀个类描述字符串
class String{
public:
    String(const char *str = NULL);
    void show(void);
private:
    char *str;
};

2.6 析构函数

6. 析构函数
⼀、引⼊
设计⼀个类描述字符串
class String{
public:
    String(const char *str = NULL);
    void show(void);
private:
    char *str;
};
⼆、代码中有什么 bug
String::String(const char *str)
{
    if(str == NULL){
        this->str = NULL;
}else{
    int len = strlen(str) + 1;
    this->str = new char[len];
    strcpy(this->str,str);
}
}
void String::show(void)
{
    for(char *p = str; p && *p ;p ++){
        cout << *p << ",ascii code:" << (int)*p << endl;
}
}
int main(int argc, const char *argv[])
{
    String str("hello world");
    str.show();
    return 0;
}

bug:

在对象⽣命周期结束的时候,没有释放堆区的内存
三、析构函数
1. 定义
        析构函数是⼀个特殊的成员函数,名字和类名相似 (~ 类名)没有返回值, 对象销毁的时候,⾃动调⽤在对象的⽣命周期内只能调⽤⼀次,保证每个对象的资源都能释放掉。
2. 实现
class String
{
public:
    String(const char *str = NULL);
    ~String();
    void show(void);
private:
    char *str;
};
    String::~String()
{
    if(str){
    delete [] str;
}
}

3. 特点

  • 析构函数函数名是在类名前⾯加上~字符
  • ⽆参数⽆返回值(但有 this 指针)
  • ⼀个类有且只有⼀个析构函数,所以肯定不能重载
  • 如果没有⼿动添加析构函数,编译器会根据需要提供⼀个默认的析构函数
  • 对象⽣命周期结束时,⾃动调⽤析构函数
  • 析构函数不能⽤ const 修饰
四、任务
找出下⾯代码中的错误,并修复它
#include <iostream>
using namespace std;
class Test{
public:
    Test(int size){
        data = new int[size];
}

    ~Test(){
        delete data;
}

    void insert(int data){
        data[index ++] = data;
}

    void show(void){
        for(int i = 0;i < index;i ++){
            cout << *data << endl;
            data ++;
}
}
private:
    int index;
    int *data;
};
int main(void)
{
    int i;
    Test *t = new Test(5);

    for(i = 0;i < 10;i ++){
        t->insert(i + 1);
}

    t->show();
    return 0;
}

2.7 拷⻉构造函数

7. 拷⻉构造函数
程序运⾏结果如下:
linux@ubuntu:~/CPP_Class/lesson4$ ./a.out
String(const char *str)
h,ascii code:104
e,ascii code:101
l,ascii code:108
l,ascii code:108
o,ascii code:111
--------------------------
h,ascii code:104
e,ascii code:101
l,ascii code:108
l,ascii code:108
o,ascii code:111
~String()
~String()
*** Error in `./a.out': double free or corruption (fasttop): 0x09c3e008 ***
Aborted (core dumped)
思考:
  • 创建两个对象只调⽤了⼀次构造函数?
  • 为什么出现了 double free 错误?
        默认构造函数编译器⾃动提供,如果我们⾃⼰⼿动编写了构造函数,编译器就不会在提供了。
        拷⻉构造函数是⼀种特殊的构造函数,它在创建对象时,是使⽤同⼀类中之前创建的对象来初始化新创建的对象。
类名 (const 类名 & 引⽤)
class String{
public:
    String(const char *str = NULL);
    String(const String &other);
    ~String(void);
    void show(void);
private:
    char *str;
};
拷⻉构造函数的参数部分必须是⼀个引⽤,不能是⼀个对象
String::String(const String &other)
{
    this->str = other.str;
}
  • 浅拷⻉只是做对象数据成员的直接赋值,编译器默认提供的拷⻉构造就是浅拷⻉的实现⽅式
  • 浅拷⻉容易导致两个对象共享⼀个同⼀个资源的情况,例如: 在 String 类中如果使⽤浅拷⻉的 形式,就会导致两个对象的 str 指向同⼀个堆区内存,在释放的时候会出现 double free 的 bug
String::String(const String &other)
{
    if(!other.str){
        this->str = other.str;
}else{
    int len = strlen(other.str) + 1;
    this->str = new char[len];
    strcpy(this->str,other.str);
}
}
深拷⻉的采⽤了重新分配资源的⽅式,让对象相互之间独⽴性更好
⼀个对象的创建需要通过另⼀个对象来初始化 **, 体现在以下三个场景:**
String str1("Hello");
String str2 = str1;
void function(String object);
void function(String &object);
void function(String *pobject);
String function(void)
{
    String object("hello");
    return object;
}
        注意: 有些时候 g++ 编译器会优化代码,不创建临时对象,看不到调⽤拷⻉构造函数的现象。可以在编译时候加上 **-fno-elide-constructors 编译参数 **,让编译器不要做此优化。
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary that
is only used to initialize another object of the same type. Specifying this
option disables that optimization,and forces G++ to call the copy constructor
in all cases.
找出如下代码的错误,编译代码的时候使⽤ **-fno-elide-constructors 编译参数, 分析构造函数 (普
通构造和拷⻉构造) 和析构函数调⽤的次数 **
#include <iostream>
using namespace std;
class Test{
public:
    Test(int size){
        cout << "Test(int size)" << endl;
        data = new int[size];
}
    Test(const Test obj){
        cout << "Test(const Test obj)" << endl;
        *this = obj;
}
    ~Test(void){
        cout << "~Test()" << endl;
        delete data;
}

private:
    int *data;
};
Test function(Test obj)
{
    Test tmp = obj;
    return tmp;
}
int main(void)
{
    Test obj1(3);
    Test obj2 = function(obj1);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值