C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
类似于洗衣服过程:
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
类是对象的抽象,是一种自定义数据类型,它用于描述一组对象的共同属性和行为。
C语言结构体只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
类(class)是结构体的拓展,不仅能够拥有成员变量,还拥有成员函数。
类是一种用于把数据和与这些数据相关的操作组合在一起的特殊数据类型。
类的定义包括了数据成员和函数成员,这些成员可以被用来描述类的抽象概念。例如,如果你想定义一个表示人的类,你可以把姓名、年龄和身高作为数据成员,而把计算 BMI 和打印个人信息作为函数成员。这样,你就可以创建多个人的对象,每个对象都有自己的数据和函数,并且可以使用类定义的函数来操作这些数据。
举个例子:
class Person {
public:
// 数据成员:
string name;
int age;
int height;
// 函数成员(也称为方法):
double calculateBMI();
void printInfo();
};
这个类定义了一个叫做 Person 的类,该类包含了三个数据成员(name、age 和 height)和两个函数成员(calculateBMI 和 printInfo)。这些成员可以被用来描述人的概念,例如:
// 创建一个 Person 类的对象:
Person p;
// 使用数据成员来设置对象的属性:
p.name = "John Doe";
p.age = 30;
p.height = 180;
// 使用函数成员来操作对象的数据:
double bmi = p.calculateBMI();
p.printInfo();
在这个例子中,我们创建了一个 Person 类的对象,并通过设置它的数据成员来为这个对象赋值。然后,我们调用了两个函数成员:calculateBMI 用来计算对象的 BMI,printInfo 用来打印对象的信息。这样,我们就可以通过类的定义和对象的创建,来操作这些对象的数据。
类的两种定义方式:
1.函数成员的声明和定义放在一起
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yS784x5e-1670567516510)(https://cdn.jsdelivr.net/gh/Eidell/image/20221026193720.png)]
2.类中函数成员的声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
void Init(int year)
{
// 这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
// 所以一般都建议这样
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 或者这样
class Date
{
public:
void Init(int year)
{
mYear = year;
}
private:
int mYear;
};
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
C++ 类是一种用于实现封装的特殊数据类型。通过类的定义,我们可以把数据成员和函数成员组合在一起,并通过访问控制关键字(例如 public 和 private)来控制这些成员的访问权限。
类的作用域是指类定义的范围,也就是类的名称在程序中有效的范围。一般来说,类的作用域包括两个部分:
内部作用域:类定义的范围,也就是类定义中类名称的作用域。在类定义的范围内,类名称可以直接使用,不需要额外的前缀。例如:
class Person {
public:
// 内部作用域:
string name;
int age;
// 函数成员的定义:
void printInfo() {
// 函数内部的作用域:
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
在这个例子中,类定义的范围就是类的内部作用域。在这个范围内,类的成员可以直接使用。
外部作用域:类定义以外的范围,也就是类名称在其他代码中的作用域。在类定义以外的范围内,类名称需要附加前缀才能使用。
类是对对象进行描述的,并不占据实际空间,只有当类实例化一个对象,该对象才占据实际空间,可以把类看作图纸,而类实例化的对象是图纸对应的建筑。
问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
我们先看下面的代码:
# include
using namespace std;
/* 定义第一个类:12个字节 */
class C1
{
private:
int a;// 4
int b;// 4
int c;// 4
};
/* 定义第二个类:12个字节 */
class C2
{
private:
int a;// 4
int b;// 4
int c;// 4
static int d;// 0
};
/* 定义第三个类:4个字节 */
class C3
{
private:
int a;// 4
static int b;// 0
public:
void setA(int a)// 0
{
this->a = a;
}
int getA()// 0
{
return this->a;
}
static void add()// 0
{
C3::b += 100;
}
};
/* 定义结构体:4个字节 */
struct S1
{
int a;
static int b;
};
int main()
{
cout << "C1类所占字节数:" << sizeof(C1) << endl; //12
cout << "C2类所占字节数:" << sizeof(C2) << endl; //12
cout << "C3类所占字节数:" << sizeof(C3) << endl; //4
cout << "S1结构体所占字节数:" << sizeof(S1) << endl; //4
return 0;
}
从上面的结果,我们得出结论:
结构体内存对齐规则
this 指针是 C++ 成员函数中的一个隐藏的指针参数,它指向当前对象。在类的函数成员中,this 指针可以用来访问当前对象的数据成员。例如,如果一个类 MyClass 有一个成员变量 x,你可以使用 this->x 来访问该变量。
this 指针是隐式地定义的,因此你不需要显式地声明它。例如,你可以在类的成员函数中直接使用 this 指针,如下所示:
class MyClass {
public:
int x;
void printX() {
// 使用 this 指针访问 x
std::cout << this->x << std::endl;
}
};
注意:
在类的静态成员函数中,不能使用 this 指针。这是因为静态成员函数不属于任何特定的对象,因此不存在当前对象。
this 指针是隐式地定义的,因此你不需要显式地声明它。
this 指针只能在类的成员函数中使用,不能在类的静态成员函数中使用。
this 指针指向当前对象,可以用来访问当前对象的成员变量。
this 指针可以作为参数传递给其他函数,表示当前对象的地址。
this 指针是一个常量指针,它的值不能被改变。
如果一个类什么都不写,那么该类称为空类,编辑器会自动生成6个默认成员函数。
默认成员函数:用户没有显式定义,编辑器自动生成的成员函数。
对于一个类,我们需要对其成员变量进行初始化,但是每一个我们都要手动初始化过于繁琐,那么,能不能能否让一个类在创建时就被编辑器自动初始化呢?
为此,我们引入了构造函数。构造函数是一个很特殊的成员函数,它的名字与类名相同,当创建一个类对象时,编辑器将自动调用该函数。保证类的每个数据成员都有一个合适的初始值。并且,该函数在类的生命周期中只会调用一次。
C++中的构造函数是一个特殊的成员函数,用于创建对象时初始化对象的成员变量。它通常与类一起使用,并且具有与类同名的函数名。构造函数的语法看起来像这样:
class MyClass {
public:
MyClass(int x, int y) {
// 初始化代码
}
};
在这个例子中,MyClass是一个类,MyClass()是这个类的构造函数,它接受两个整型参数x和y。构造函数中的代码用于初始化类的成员变量。
在C++中,构造函数被自动调用,无需显式地调用它。当创建一个类的新实例时,构造函数将自动被调用,并且可以用来初始化新创建的对象。例如:
MyClass obj(10, 20);
在这个例子中,当创建obj对象时,构造函数被自动调用,并使用传递给构造函数的参数(10和20)来初始化obj的成员变量。
总的来说,构造函数是一种用于在创建对象时初始化对象的成员变量的特殊函数。它与类一起使用,并在创建类的新实例时自动调用。
//构造函数的重载
class Date
{
public:
void Print();
Date(int year = 2022, int month = 5, int day = 20) //构造函数1
{
_year = year;
_month = month;
_day = day;
}
Date(int year,int month, int day) //构造函数2
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date tmp; //编译器自动调用构造函数
Date x(2022, 5, 20); //调用构造函数1
return 0;
}
析构函数是 C++ 中特殊的一种成员函数,它会在某个对象的生命周期结束时自动调用。析构函数的名字与类名相同,但是它的名字前面带有一个波浪线 ~。例如,如果有一个类叫 MyClass,那么它的析构函数就是 ~MyClass。
析构函数通常用来做清理工作,例如释放动态分配的内存、关闭文件等等。举个例子,如果在某个类的构造函数中动态分配了一块内存,那么在析构函数中可以释放这块内存,以避免内存泄露。
下面是一个简单的析构函数的例子:
class MyClass
{
public:
// 构造函数
MyClass()
{
// 在构造函数中动态分配一块内存
m_data = new int[1024];
}
// 析构函数
~MyClass()
{
// 在析构函数中释放动态分配的内存
delete[] m_data;
}
private:
int *m_data; // 用来存储动态分配的内存
};
当一个 MyClass 类的对象创建后,它的构造函数就会自动调用,用来分配内存。而当这个对象的生命周期结束时,它的析构函数也会自动调用,用来释放内存。这样就可以确保在对象的生命周期结束时总是能够及时释放动态分配的内存。
注意:如果一个类没有定义析构函数,那么编译器会自动为这个类生成一个默认的析构函数。默认的析构函数什么都不做,它只是简单地结束对象的生命周期。如果类中没有动态分配内存等资源,那么使用默认的析构函数就足够了。但是,如果类中有动态分配的内存或者其他资源,那么必须自己定义析构函数来释放这些资源,以避免内存泄露等问题。
析构函数是特殊的成员函数,其特征如下:
~类名()
。在创建对象时,我们能否,复制出一个一模一样的对象呢?拷贝构造函数就能完成这件事。
拷贝构造函数是 C++ 中另一种特殊的构造函数,它的作用是在创建对象时,将另一个同类型的对象作为模板,复制它的数据成员到新创建的对象中。拷贝构造函数的名字与类名相同,它的语法如下:
class MyClass
{
public:
// 拷贝构造函数
MyClass(const MyClass &other)
{
// 将 other 对象中的数据成员复制到新创建的对象中
}
};
拷贝构造函数通常用于实现深拷贝,即复制一个对象时,不仅复制它的数据成员,还要复制它所持有的所有资源,例如动态分配的内存、打开的文件等等。这样可以确保新创建的对象与原对象之间互不影响,并且在删除新对象时不会产生内存泄露等问题。
拷贝构造函数只有一个形参(不包括this),该形参是本类对象的引用,一般最好要加const修饰。当用已经存在的类对象去创建一个新类对象时,编辑器自动调用该函数。
拷贝构造只有一个参数,且必须是同类对象的引用。如果是传值参数,将会发生无限循环。因为形参是实参的一份拷贝。当把这个形参传入时,又需要拷贝构造。就这样无限循环。而且最好加const修饰,防止权限的放大。
拷贝构造函数使用形式如下:
classname a; //创建类对象a
classname b(a); //以对象a为蓝本创建新对象b,也可以写成classname b = a;
如下所示:
class Time
{
public:
Time() //构造函数
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time& t) //定义拷贝构造函数
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
}
运算符重载是指在 C++ 中,允许将已有的运算符(如 +、-、* 等)用于自定义数据类型(例如类或结构体)。这样,我们就可以使用已有的运算符来操作这些自定义数据类型,这样做可以提高代码的可读性和可维护性。
具体来说,运算符重载就是定义一个函数来改变运算符的含义,以适应特定的数据类型。这个函数称为重载运算符函数。
运算符重载语法格式如下:
// 定义重载运算符函数
返回类型 operator运算符(参数列表) {
// 函数体
}
例如,如果要重载 + 运算符,可以使用如下的语法:
class Vector2D {
public:
double x, y;
// 定义重载运算符函数
Vector2D operator+(const Vector2D& v) const {
Vector2D result;
result.x = x + v.x;
result.y = y + v.y;
return result;
}
};
除了函数名称中的operator关键字,运算符重载函数与普通函数没有区别。
非成员函数:
单目运算符:参数表中只有一个参数
双目运算符:参数表中只有两个参数
成员函数:
单目运算符:参数表中没有参数
双目运算符:参数表中只有一个参数
赋值运算符重载是指在 C++ 中,可以定义一个函数来改变赋值运算符 = 的含义,以适应特定的数据类型。这样,我们就可以使用 = 运算符来赋值自定义的数据类型,而不用再写长长的赋值语句。
赋值运算符重载格式
class Date
{
public:
Date& operator=(const Date& d) //引用提高效率
{
if(this != &d) //检测是否自己给自己赋值
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year ;
int _month ;
int _day ;
};
特性
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数。
2. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符
重载完成赋值。
如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必
须要实现。
cin 和 cout 是C++标准库中的两个流对象,它们分别用于读取和写入数据。
cin是标准输入流,通常指向键盘。我们可以使用它来读取用户输入的数据,例如:
int x;
cin >> x; // 读取一个整数
cout是标准输出流,通常指向屏幕。我们可以使用它来向用户输出数据,例如:
cout << "Hello, world!" << endl; // 输出一行文本
注意的是,在使用cin和cout之前,我们需要在代码中包含iostream头文件。
cout和cin流对象支持多种类型的数据的输入和输出,这是怎样实现的呢❓
这是通过运算符重载实现的,对内置类型的输入和输出已经在库文件中写好了。所以当我们想输入和输出一个自定义类型对象,就需要自己写一个运算符重载。
假如我们现在想输出一下日期类对象,该怎么做❓
先看如下代码:
class Date
{
public:
// cout流对象的类型为ostream
void operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year;
int _month;
int _day;
}
Date d1;
这样写存在问题,那就是重载运算符的左操作数是输出的对象,我们只能这么使用
d1<
看起来很难受。
所以记住:<< 和 >> 重载一般不作为成员函数,因为this指针抢了第一个位置,使用起来很别扭。
如何改进❓
写成全局函数,这样可以自定义两个参数位置。
这又产生了另一个问题,如何访问类的私有成员❓
三种方式:
如果定义了头文件Date.h和源文件test.cpp,Date.cpp。我们在Date.h中定义一个全局函数,在test.cpp和Date.cpp都Date.h,,这样我们就会出现重复定义的问题。
解决方法:
方法1:不要在头文件中实现函数,而将声明和实现分别写在.h和.cpp中。
方法2:如果一定要在头文件中实现函数,请在.h中实现的函数加上static。
2.设置成员函数get()来访问成员变量
3.友元声明
实际使用中我们常常会连续输出,所以要返回一个ostream类型对象
所以如下定义:
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
先创建一个日期类常量对象
const Date d2(2022,10,10);
调用打印函数
d2.Print();
结果报错,为什么❓
成员函数Print() 存在一个this指针,类型为 Date const * ,const修饰指针本身
&d2指向内容不可修改。的类型const Date * ,const修饰指向的内容。
当传递指针和引用类型时,要考虑权限问题。
显然,这里权限被放大了,this指针指向内容可以修改,&d2指向内容不可修改。
如何解决❓
用 const 修饰成员函数,实际就是修饰* this
void Print() const
{
......
}
this指针类型变为const Date * cosnt this
,指向内容就不可修变,便不存在权限问题了。
补充
const修饰成员变量:
const修饰成员变量表示成员常量,只能在初始化列表中赋值,可以被const和非const成员函数调用,但不能修改其值。
const修饰成员函数:
使用const修饰的成员函数称为常成员函数。与修饰成员变量不同的是,修饰成员函数时,const位于成员函数的后面,其格式如下:
返回值类型 函数名() const;
常成员函数特点:
- 只能访问类的成员变量,而不能修改类的成员变量
- 只能调用类的常成员函数,而不能调用类的非常成员函数
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值
如下所示:
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因初始化只能初始化一次,而构造函数体内可以多次赋值(不符合初始化的基本原则)。
总结:
构造函数函数体赋值相当于是成员变量初始化后的赋值。
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
class B
{
public:
B()
{
_n = 10;
}
private:
const int _n; //声明
int _m = 1; //缺省值
}
B b;
编译器报错
因为成员变量必须在初始化列表定义。
一句话概括成员变量初始化顺序
:如果成员变量有缺省值,又有初始化列表和函数体赋值,对象创建时会先走初始化列表,再走函数体赋值,最后走成员缺省值。
Date d1(2022); //这是构造
Date d3(d1); //这是拷贝构造
Date d3 = d1; //这是拷贝构造
Date d2 = 2022; //这是?根据2022构造一个临时对象,再由这个临时对象拷贝构造另一个对象
const Date& d5 = 2022; //这就是隐式类型转换
如果我不想这样的转换发生,就用 explicit 关键字修饰
什么样的函数支持隐式类型转换
Date(int year)
Date(int year, int month = 1, int day = 1)
概念
explicit 关键字是 C++ 中的一个修饰符,用于修饰构造函数。如果一个构造函数被 explicit 修饰,它就只能被显式调用,不能被隐式调用。
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化(规定)
class A
{
public:
A()
{
++_count;
}
A(const A& t)
{
++_count;
}
static int GetACount()
{
return _count;
}
private:
static int _count;
};
int A::_count = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
int main()
{
TestA();
return 0;
}
说明:全局变量可以在多个地方被修改,故C++尽量避免使用全局变量,
我们想让变量生命周期和全局变量一样,但作用域变小,这就出现了静态变量。
在类中的静态变量属于类,并且类的每个对象共享,但在类的外面初始化。
生命周期: 变量从定义到销毁的时间范围。全局变量的生命周期是整个程序运行期间,而存放在栈区的变量则随着函数作用域结束导致出栈而销毁,除了静态变量之外的局部变量都存放于栈中。
作用域: 变量的可见代码域(块作用域,函数作用域,类作用域,程序全局作用域)。
静态成员变量
问题:静态成员函数可以调用非静态成员函数吗?
不能,对于静态成员函数并不是某个对象的具体实例,也就没有this指针,无法调用非静态成员函数
可以,静态成员函数为所有类对象所共享
友元分为:友元函数和友元类
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
问题引入:
现在尝试去重载operator<<
输出类的成员变量,然后发现没办法将operator<<
重载成成员函数。因为 cout 的输出流对象和隐含的 this 指针在抢占第一个参数的位置。所以要将 operator<< 重载成
全局函数,以消除 this 指针。但这又会导致类外没办法访问成员(除非写get()函数),此时就需要友元来解决。
使用友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在
类的内部声明,声明时需要加friend关键字
示例:
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
1.友元函数可访问类的私有和保护成员,但不是类的成员函数
2. 友元函数不能用const修饰
3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4. 一个函数可以是多个类的友元函数
5. 友元函数的调用与普通函数的调用原理相同
问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的
输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作
数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成
全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
中的私有成员变量
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;Time _t;
};
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限
内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
总结一下定义对象的几种方式:
// 有名对象
A aa0;
A aa1(1);
A aa2 = 2;
// 匿名对象
A();
A(3);
概念:
匿名对象是指在定义对象时不指定对象名称的对象。其生命周期只有一行。
示例:
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution {
public:
int Sum_Solution(int n) {
//...
return n;
}
};
int main()
{
A aa1;
// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
//A aa1();
// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
A();
A aa2(2);
// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说
Solution().Sum_Solution(10);
return 0;
}
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还
是非常有用的。
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
void f1(A aa)
{}
A f2()
{
A aa;
return aa;
}
int main()
{
// 传值传参
A aa1;
f1(aa1);
cout << endl;
// 传值返回
f2();
cout << endl;
// 隐式类型,连续构造+拷贝构造->优化为直接构造
f1(1);
// 一个表达式中,连续构造+拷贝构造->优化为一个构造
f1(A(2));
cout << endl;
// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
A aa2 = f2();
cout << endl;
// 一个表达式中,连续拷贝构造+赋值重载->无法优化
aa1 = f2();
cout << endl;
return 0;
}
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现
实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创
建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:
在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那
些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化
具体的对象