一 基本概念
类、对象、成员变量、成员函数。
面向对象三大概念:封装、继承、多态。
二 类的封装
1 封装,是面向对象程序设计最基本的特性。把数据(属性)和函数(操作)合成一个整体,
2 类成员的访问控制
1)public修饰的成员变量 方法 在类的内部 类的外部都能使用
2)protected:修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ;其他类的外部不能被使用
3) private: 修饰的成员变量方法 只能在类的内部使用 不能在类的外部
三 构造函数
1 C++中的类可以定义与类名相同的特殊成员函数。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。
2 构造函数分类,建立对象时调用。
1)无参数构造函数: Test t1, t2;
2)带参数的构造函数
a)括号法 :Test5 t1(10);
b) 等号法:Test5 t2 = (20, 10)
c 直接调用:Test5 t3 = Test5(30);
3)拷贝构造函数
a) 用一个对象去初始化另一个类:Test4 t2 = t1; //用t1来初始化 t2
b) 括号法:同上,写法不同 Test4 t2(t1); //用t1对象 初始化t2对象
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 () { return X ; } int GetY () { return Y ; }
private : int X , 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构造函数
}
void main()
{
playobj();
cout<<"hello..."<<endl;
system("pause");
return ;
}
d) 用匿名对像赋值其它对象
#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 () { return X ; } int GetY () { return Y ; }
private : int X , Y ;
} ;
Location g()
{
Location A(1, 2);
return A;
}
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()
{
objplay3();
objplay4();
cout<<"hello..."<<endl;
system("pause");
return ;
}
3 构造函数的调用规则
1) 当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2) 当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3)当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数。
4)默认拷贝构造函数成员变量简单赋值
四 析构函数
1)C++中的类可以定义一个特殊的成员函数清理对象
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
五 深拷贝与浅拷贝
#include <iostream>
using namespace std;
class Name
{
public:
Name(const char *myp)
{
m_len = strlen(myp);
m_p =(char *) malloc(m_len + 1); //
strcpy(m_p, myp);
}
//解决方案: 手工的编写拷贝构造函数 使用深copy
Name(const Name& obj1)
{
m_len = obj1.m_len;
m_p = (char *)malloc(m_len + 1);
strcpy(m_p, obj1.m_p);
}
~Name()
{
if (m_p != NULL)
{
free(m_p);
m_p = NULL;
m_len = 0;
}
}
protected:
private:
char *m_p ;
int m_len;
};
//对象析构的时候 出现coredump
void objplaymain()
{
Name obj1("abcdefg");
Name obj2 = obj1; //C++编译器提供的 默认的copy构造函数 浅拷贝
Name obj3;
obj3 = obj1; // C++编译器提供的 等号操作 也属 浅拷贝
}
void main91()
{
objplaymain();
cout<<"hello..."<<endl;
system("pause");
return ;
}
六 对象初始化列表
1 出现原因
1)在B类中集合了一个A类对象(A类设计了构造函数),根据构造函数的调用规则,设计了构造函数,就要调用。没有机会初始化A,新的语法派上用场。即对象初始化列表。
2)当类成员中含有一个const成员变量时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化。
3) 拷贝构造函数后面也可以跟初始化列表。
2 语法规则
Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
{
// some other assignment operation
}
3 执行顺序
1) 成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
2) 初始化列表先于构造函数的函数体执行
4构造函数和析构函数的调用顺序研究
1)当类中有成员变量是其它类的对象时,首先调用成员变量的构造函数,调用顺序与声明顺序相同;之后调用自身类的构造函数
2)析构函数的调用顺序与对应的构造函数调用顺序相反
3)初始化列表用来给const属性赋值。
#include <iostream>
using namespace std;
class A
{
public:
A(int _a)
{
a = _a;
cout << "构造函数" << "a" << a << endl;
}
~A()
{
cout << "析构函数" << "a" << a << endl;
}
protected:
private:
int a;
};
//1 构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
B(int _b1, int _b2) : a1(1), a2(2), c(0)
{
}
B(int _b1, int _b2, int m, int n) : a1(m), a2(n), c(0)
{
b1 = _b1;
b2 = _b2;
cout <<"B的构造函数"<<endl;
}
~B()
{
cout<<"B的析构函数" <<endl;
}
protected:
private:
int b1;
int b2;
A a2;
A a1;
const int c;
};
//2 先执行 被组合对象的构造函数
//如果组合对象有多个,按照声明顺序, 而不是按照初始化列表的顺序
//析构函数 : 和构造函数的调用顺序相反
//3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系.
//4 初始化列表 用来 给const 属性赋值
void obj10play()
{
//A a1(10);
//B ojbB(1, 2);
//1参数传递
B ojbB2(1, 2,3, 4);
//2 调用顺序
return ;
}
void main100()
{
obj10play();
system("pause");
}
七 综合练习
//对象做函数参数
//1 研究拷贝构造
//2 研究构造函数,析构函数的调用顺序
//总结 构造和析构的调用顺序
#include "iostream"
using namespace std;
class ABCD
{
public:
ABCD(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
int getA()
{
return this->a;
}
protected:
private:
int a;
int b;
int c;
};
class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
cout<<"MyD()"<<endl;
}
~MyE()
{
cout<<"~MyD()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
printf("MyD(const MyD & obj)\n");
}
protected:
public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};
int doThing(MyE mye1)
{
printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA());
return 0;
}
int run2()
{
MyE myE;
doThing(myE);
return 0;
}
int run3()
{
printf("run3 start..\n");
//ABCD(400, 500, 600); //临时对象的生命周期
ABCD abcd = ABCD(100, 200, 300);
//若直接调用构造函数呢?
//想调用构造函数对abc对象进行再复制,可以吗?
//在构造函数里面调用另外一个构造函数,会有什么结果?
printf("run3 end\n");
return 0;
}
int main()
{
run2();
run3();
system("pause");
return 0;
}
在构造中调用构造是一种危险行为。
#include "iostream"
using namespace std;
//构造中调用构造是危险的行为
class MyTest
{
public:
MyTest(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
MyTest(int a, int b)
{
this->a = a;
this->b = b;
MyTest(a, b, 100); //产生新的匿名对象
}
~MyTest()
{
printf("MyTest~:%d, %d, %d\n", a, b, c);
}
protected:
private:
int a;
int b;
int c;
public:
int getC() const { return c; }
void setC(int val) { c = val; }
};
int main()
{
MyTest t1(1, 2);
printf("c:%d", t1.getC()); //此时输出的是一个乱码。因为直接调用一个构造函数会产生一个匿名对象,其生命周期只有被调用的那一行,然后被析构。生成的匿名对象和我们定义的那个t1没有什么关系。
system("pause");
return 0;
}
八 对象的动态建立和释放
C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
注意: new和delete是运算符,不是函数,因此执行效率高。
class Test
{
public:
Test(int _a)
{
a = _a;
cout<<"构造函数执行" <<endl;
}
~Test()
{
cout<<"析构函数执行" <<endl;
}
private:
int a;
};
//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数 delete操作符 能执行类的析构函数
void main()
{
//c
Test *pT1 = (Test *)malloc(sizeof(Test));
free(pT1);
//c++
Test *pT2 = new Test(10);
delete pT2;
cout<<"hello..."<<endl;
system("pause");
}