本文主要学习的是C++编译器管理类和对象的方法,对类对象的生命周期的管理,对象创建, 使用,销毁等
C++面向对象的模型,C++面向对象多态原理。最后学会利用C++面向对象的三大特性,封装,继承,多态
进行面向抽象类(接口)编程,把C++语言作为工具,实现我们需要的功能。
封装:将客官事物抽象成类,每个类对自身数据和方法进行保护(public,private,protected)
两层含义:(把属性和方法进行封装,对属性和方法进行访问控制)。
类是一个数据类型,类是抽象的,对象是一个具体的变量,占用内存空间。
struct 和 class 关键字区别
在用struct定义类时,所有成员的默认属性为public
在用class定义类时,所有成员的默认属性为private
C++面向对象的思想:在设计类时候,用C语言增加的全局函数,可以把该全局函数成为类的成员函数。
1 为什么需要构造函数?
因为假设我们定义的是对象数组,则如果手工去初始化,非常麻烦,复杂,
交给编译器去处理,在创建对象时,自动执行构造函数对数据成员进行初始化。
2 构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数。
手动调用:在一些情况下则需要手工调用构造函数。
3 析构函数
C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫 析构函数
语法:~ClassName()
析构函数没有参数,也没有任何的返回类型的声明
析构函数在对象销毁时自动调用
4 构造函数的分类和调用
构造函数分为: 无参构造函数,有参构造函数,拷贝构造函数
无参构造函数
调用方法: Test t1,t2;
有参构造函数
有3种调用方法:
括号法、等号法、直接调用构造函数法
例如:如下程序
Test2 t1;//调用无参构造函数
//括号法
Test2 t1(2,3); //调用是带两个整形参数的构造函数
// =号法
Test2 t2 = (3,4); //C++对等号操作符进行功能增强 ,逗号表达式最右边的值,作为整个表达式的值
Test2 t3 = 5;
//3 直接调用构造函数 手动的调用构造函数
Test2 t4 = Test2(1,2); //将产生一个匿名对象,的去和留 这里只调用一次构造函数
//然后把匿名对象直接转成t4,不需要调用拷贝构造函数, 只是完成t4的初始化
t1 = t4; //t1对象已经存在,这里的意思是,把t4拷贝给t1,赋值操作,没有调用拷贝构造函数
//对象的初始化和对象的赋值,是两个不同的概念。
这里需要注意的是: 第三种调用方法,手动的去选择调用哪一个构造函数,将产生一个匿名对象,
而对于Test t4 = Test2(1,2) 这样的对象初始化,是不需要调用拷贝构造函数的。
t1 = t4 ; //只是把t4浅拷贝(位拷贝)给t1 而已。
#include <iostream>
using namespace std;
class Test2
{
public:
Test2()
{
m_a = 0;
m_b= 0;
cout << "无参的构造函数" << endl;
}
Test2(int a)
{
m_a = a;
m_b = 0;
cout << "a= " << a << endl;
}
Test2(int a, int b) //有参构造函数,调用它,有三种方法
{
m_a = a;
m_b = b;
cout << "有参的构造函数" << endl;
}
//拷贝构造函数(copy构造函数)
Test2(const Test2 &obj)
{
cout << "我也是构造函数" << endl;
}
private:
int m_a;
int m_b;
};
int main()
{
//Test2 t1;//调用无参构造函数
//括号法
Test2 t1(2,3);
// =号法
Test2 t2 = (3,4); //C++对等号操作符 进行功能增强 ,逗号表达式最右边的值,作为整个表达式的值
Test2 t3 = 5;
//3 直接调用构造函数 手动的调用构造函数
Test2 t4 = Test2(1,2); //将产生一个匿名对象,的去和留 这里只调用一次构造函数
//然后把匿名对象直接转成t4,不需要调用拷贝构造函数 完成t4的初始化
t1 = t4; //t1对象已经存在,这里的意思是,把t4拷贝给t1,赋值操作,不需要调用拷贝构造函数
//对象的初始化和对象的赋值,是两个不同的概念。
system("pause");
return 0;
}
5 拷贝构造函数调用时机
拷贝(copy)构造函数的四种调用场景。
用一个对象初始化另一个对象时,调用拷贝构造函数,有 等号(=)和括号法两种场景。
#include <iostream>
using namespace std;
class Test4
{
public:
Test4()
{
m_a = 0;
m_b = 0;
cout << "无参的构造函数" << endl;
}
Test4(int a)
{
m_a = a;
m_b = 0;
cout << "a= " << a << endl;
}
Test4(int a, int b)
{
m_a = a;
m_b = b;
cout << "有参的构造函数" << endl;
}
//拷贝构造函数(copy构造函数)
Test4(const Test4 &obj)
{
m_b = obj.m_b + 100;
m_a = obj.m_a + 100;
cout << "我也是构造函数" << endl;
}
public:
void printT()
{
cout << "普通成员函数" << endl;
cout << "m_a = " << m_a << endl;
cout << "m_b = " << m_b << endl;
}
private:
int m_a;
int m_b;
};
// 1 拷贝构造函数 用一个对象去初始化另外一个对象
int main()
{
Test4 t1(1,2);
Test4 t0(1,2);
//赋值=操作,不会调用拷贝构造函数。
t0 = t1; //用t1给t0赋值的操作 和 初始化是两个不同的概念,这里只是t1给t0的一个浅拷贝而已
//第一种调用场景
Test4 t2 = t1; //用T1来初始化t2
//第二种调用场景
Test4 t3(2,3);
//括号法,也完成了用t3对象初始化t4对象,调用拷贝构造函数
Test4 t4(t3);
system("pause");
return 0;
}
第三种场景: 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数。
#include <iostream>
using namespace std;
class Location
{
public:
Location(int xx = 0, int yy =0)
{
X = xx; Y = yy;
cout << "构造函数 对象 " << endl;
}
Location(const Location &obj)//COPY 构造函数
{
X = obj.X;
Y = obj.Y;
}
~Location()
{
cout << X <<", " << Y << "对象销毁" << endl;
}
int GetX()
{
return X;
}
int GetY()
{
return Y;
}
private:
int X;
int Y;
};
//业务函数
void f(Location p) //
{
cout << p.GetX() << endl;
}
void playobj()
{
Location a(1,2);
Location b = a;
cout << "b对象已经初始化完毕" << endl;
f(b); //b实参去初始化形参p ,系统自动调用copy构造函数
}
int main()
{
playobj();
system("pause");
return 0;
}
第四种调用场景:当函数的返回值是类对象时,系统自动调用拷贝构造函数。
这种情况稍微复杂点,涉及到匿名对象的创建和析构
首先 返回类对象时,把类对象返回时,通过拷贝构造函数产生匿名对象。
然后再看,怎么接这个匿名对象,即:
如果用匿名对象初始化另一个同类型的对象,此时C++编译器把匿名对象转成有名的对象,不会调用拷贝构造函数
如果用匿名对象赋值给另一个同类型的对象,此时C++编译器把匿名对象浅拷贝给新对象后,接着析构匿名对象。
#include <iostream>
using namespace std;
class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ; Y = yy ; cout << "Constructor Object.\n" ;
}
//copy构造函数 完成对象的初始化
Location(const Location & obj) //copy构造函数
{
X = obj.X; Y = obj.Y;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl ;
}
int GetX ()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return X ; }
<span style="white-space:pre"> </span>int GetY ()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return Y ; }
private :
int X , Y ;
} ;
//g函数 返回类对象
//结论1 : 函数的返回值是类对象时, 返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)
//
//结论2: 有关 匿名对象的去和留
//如果用匿名对象 初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象 赋值给 另外一个同类型的对象, 匿名对象 被析构
//
//你这么写代码,设计编译器的大牛们:
//我就给你返回一个新对象(没有名字 匿名对象)
Location g()
{
Location A(1, 2);
return A;//当返回类对象A时,用对象A,调用拷贝构造函数后创建匿名对象,接着函数g()结束后,析构对象A
}
//
void objplay2()
{
g();
}
//
void objplay3()
{
//用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
Location m = g();
printf("匿名对象,被扶正,不会析构掉\n");
cout<<m.GetX()<<endl;;
}
void objplay4()
{
//用匿名对象 赋值给 m2后, 匿名对象被析构
Location m2(1, 2);
m2 = g();
printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
cout<<m2.GetX()<<endl;;
}
void main()
{
//objplay2();
//objplay3();
objplay4();
cout<<"hello..."<<endl;
system("pause");
return ;
}
6 构造函数调用规则
当类中没有定义任何一个构造函数时,C++编译器会提供默认无参构造函数和默认拷贝构造函数
当类中定义了拷贝构造函数时,C++编译器不会提供无参构造函数(即默认拷贝构造函数和默认构造函数不存在了)
而默认拷贝构造函数,只是简单进行一个位拷贝(浅拷贝)而已。
一句话: 只要你写了构造函数,那么你必须用。
因为C++编译器提供的默认拷贝构造函数只是一个浅拷贝,在某些情况下,如类的数据成员中友指针类型时,就会带来数据安全方面的一些隐患。因为我们需要自定义一个拷贝构造函数,实现深拷贝操作。