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;
}

1449

被折叠的 条评论
为什么被折叠?



