类中的声明定义问题
-
看有没有开空间,开空间就是定义,没开就是声明
-
int age; // 定义 class Person { public: void PrintPersonInfo(); // 声明 private: int _age; char _name[20]; // 声明 char _gender[10]; };
-
区分extern和static定义变量时的问题
- 在Person.h中有如下代码
-
int age; // 定义 class Person { public: void PrintPersonInfo(); // 声明 private: int _age; char _name[20]; // 声明 char _gender[10]; };
- Person.cpp
-
#include "Person.h" void Person::PrintPersonInfo() { cout << "name:" << _name << endl << "age:" << _age << endl; }
- main.cpp
-
#include "Person.h" int main() { Person p1; return 0; }
- 此时运行代码会出现链接错误(fatal error LNK1169: 找到一个或多个多重定义的符号),因为age会在person.cpp和main.cpp展开,都会放到符号表,链接时就会有两个age,所以.h文件中要谨慎地定义全局变量
-
解决
- 只需要在Peroson.h中将int age;改为
-
extern int age; // 此时age为声明,所有文件可见,可以在别的.cpp中定义它(在全局作用域中), // 但是不能在两个同时包含Peroson.h中的.cpp文件中定义它
- 也可以这样改
-
static int age = 18; // 这个意思是age在当前文件可见,即age不会放到每一个.cpp的符号表 // 从而不会发生重定义错误
- 如果使用static定义age,分别在Person.cpp和main.cpp打印下地址
-
// Person.cpp cout << "Peroson.cpp static:" << &age << endl; // main.cpp cout << "main.cpp static:" << &age << endl; main.cpp static:00007FF75DFCE170 Peroson.cpp static:00007FF75DFCE174
- 可知,用static时,在不同的.cpp文件中不是同一个变量
- 如果使用extern声明age,打印下面地址
-
// Person.cpp cout << "Peroson.cpp extern:" << & age << endl; // main.cpp cout << "main.cpp extern:" << &age << endl; main.cpp extern:00007FF73BD1E000 Peroson.cpp extern:00007FF73BD1E000
- 可知,用extern时,在不同的.cpp文件中是同一个变量
-
类对象的大小如何计算?
- 假设有如下代码
-
class A { public: void Print_A() { cout << _a << endl; } private: char _a; }; int main() { A a; cout << sizeof(a); }
-
结果是1,只有char _a的大小,由此可以计算A大小并没有算上Print_A函数
-
类对象存储方式
-
- 只保存成员变量,成员函数存放在公共的代码段
{:height 601, :width 742}
- 只保存成员变量,成员函数存放在公共的代码段
-
int main() { A a1; A a2; a1.Print_A(); a2.Print_A(); } // 上面代码的汇编为 a1.Print_A(); 00007FF712901E7D lea rcx,[a1] 00007FF712901E81 call A::Print_A (07FF71290146Ah) a2.Print_A(); 00007FF712901E86 lea rcx,[a2] 00007FF712901E8A call A::Print_A (07FF71290146Ah)
- 由此可以看到,调用的Print_A为同一个函数
- 看如下代码
-
class A { public: void Func() { cout << this << endl; cout << "A Func" << endl; } void Func2() { cout << this << endl; cout << this->_a << endl; } private: char _a; int _i; }; int main() { A* ptr = nullptr; ptr->Func(); // 1 ptr->Func2(); // 2 return 0; }
- 上面程序1是正常运行的,因为在编译链接时就根据函数名去公共代码区找到函数的地址,call函数地址
- 2会运行崩溃,因为解引用了this指针,this是nullptr
-
this指针存在哪里?
-
栈区,因为他是一个形参.有些情况下编译器会优化,vs下面传递this指针,是通过ecx寄存器传递的,这样传递this变量提高效率
构造函数的特点
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。其特征如下:
-
函数名与类名相同。
-
无返回值。
-
对象实例化时编译器自动调用对应的构造函数。
-
构造函数可以重载。
-
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
默认的无参构造函数的规则是:
a.内置类型的成员不做处理
b.自定义类型成员回去回去调用它的默认构造函数
-
6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
析构函数
对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
其特性如下:
-
析构函数名是在类名前加上字符 ~.
-
无参数无返回值类型。
-
一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
默认析构函数的规则是:
a.内置类型不做处理
b.自定义类型成员回去调用它的析构函数
-
对象生命周期结束时,C++编译系统系统自动调用析构函数。
拷贝构造函数
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。
其特性如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类 类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
class Date
{
public:
Date(int year = 2023, int month = 9, int day = 14)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
this->_day = d._day;
this->_month = d._month;
this->_year = d._year;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
Date d3 = d1;
return 0;
}
- 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,否则就是浅拷贝。