基本概念
1)类、对象、成员变量、成员函数
2)面向对象三大概念
封装、继承、多态
类的封装
1)封装(Encapsulation)
A)封装,是面向对象程序设计最基本的特性。把数据(属性)和函数(操作)合成一个整体,这在计算机世界中是用类与对象实现的。
B)封装,把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
备注:有2层含义(把属性和方法进行封装对属性和方法进行访问控制)
C++中类的封装
成员变量,C++中用于表示类属性的变量
成员函数,C++中用于表示类行为的函数
2)类成员的访问控制
在C++中可以给成员变量和成员函数定义访问级别
Public修饰成员变量和成员函数可以在类的内部和类的外部被访问
Private修饰成员变量和成员函数只能在类的内部被访问
3)struct和class关键字区别
在用struct定义类时,所有成员的默认属性为public
在用class定义类时,所有成员的默认属性为private
对象的构造和析构
前言
创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。注意,类的数据成员是不能在声明类时初始化的。
为了解决这个问题,C++编译器提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。
造函数和析构函数的概念
1.有关构造函数
1构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数
有关析构函数
3)析构函数定义及调用
1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
4)析构函数调用机制
C++编译器自动调用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Test
{
public:
Test() //无参数 构造函数
{
a = 10; //作用完成对属性的初始化工作
p = (char *)malloc(100);
strcpy(p, "aaaaffff");
cout<<"我是构造函数 被执行了"<<endl;
}
void print()
{
cout<<p<<endl;
cout<<a<<endl;
}
~Test() //析构函数
{
if (p != NULL)
{
free(p);
}
cout<<"我是析构函数,被调用了" <<endl;
}
protected:
private:
int a ;
char *p;
};
//给对象搭建一个舞台,研究对象的行为
void objplay()
{
//先创建的对象 后释放
Test t1;
t1.print();
printf("分隔符\n");
Test t2;
t2.print();
}
void main11()
{
objplay();
cout<<"hello..."<<endl;
system("pause");
return ;
}
构造函数的分类及调用
1无参数构造函数
调用方法:Test t1, t2;
2有参构造函数
//有参数构造函数的三种调用方法
class Test5
{
private:
int a;
public:
//带参数的构造函数
Test5(int a)
{
printf("\na:%d", a);
}
Test5(int a, int b)
{
printf("\na:%d b:%d", a, b);
}
public:
};
int main55()
{
Test5 t1(10); //c++编译器默认调用有参构造函数 括号法
Test5 t2 = (20, 10); //c++编译器默认调用有参构造函数 等号法
Test5 t3 = Test5(30); //程序员手工调用构造函数 产生了一个对象 直接调用构造构造函数法
system("pause");
return 0;
}
3.拷贝构造函数调用时机
#include "iostream"
using namespace std;
class AA
{
public:
AA() //无参构造函数 默认构造函数
{
cout<<"我是构造函数,自动被调用了"<<endl;
}
AA(int _a) //无参构造函数 默认构造函数
{
a = _a;
}
AA(const AA &obj2)
{
cout<<"我也是构造函数,我是通过另外一个对象obj2,来初始化我自己"<<endl;
a = obj2.a + 10;
}
~AA()
{
cout<<"我是析构函数,自动被调用了"<<endl;
}
void getA()
{
printf("a:%d \n", a);
}
protected:
private:
int a;
};
//单独搭建一个舞台
void ObjPlay01()
{
AA a1; //变量定义
//赋值构造函数的第一个应用场景
//用对象1 初始化 对象2 调用a2的copy构造函数
AA a2 = a1; //定义变量并初始化 //初始化法
a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy
}
第二个应用场景
//单独搭建一个舞台
void ObjPlay02()
{
AA a1(10); //变量定义
//赋值构造函数发表文章的第一个应用场景
//用对象1 初始化 对象2
AA a2(a1); //定义变量并初始化 //括号法
//a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy
a2.getA();
}
//注意:初始化操作 和 等号操作 是两个不同的概念
#include<iostream>
using namespace std;
class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx;
Y = yy;
cout << "Construct Object\n";
}
//copy构造函数 完成对象初始化
Location(const Location &obj)
{
X = obj.X;
Y = obj.Y;
}
~Location()
{
cout << X << "," << Y << "Object destroyed" << endl;
}
int GetX()
{
return X;
}
int GetY()
{
return Y;
}
private:
int X;
int Y;
};
//业务函数 形参是一个元素 void f(Location p)
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;
}
第4个调用场景
#include<iostream>
using namespace std;
class Location
{
public:
Location(int xx,int yy)
{
X = xx;
Y = yy;
}
Location(Location &obj)
{
X = obj.X;
Y = obj.Y;
}
private:
int X;
int Y;
};
//结论1 : 函数的返回值是一个元素 (复杂类型的),
//返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)
Location g()//g函数 返回一个元素
{
Location A(1, 2);
return A;
}
//结论2: 有关 匿名对象的去和留
//如果用匿名对象 初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象 赋值给 另外一个同类型的对象, 匿名对象 被析构
void objplay2()
{
g();
}
void objplay3()
{
Location m = g();
printf("匿名对象被扶正,不会被析构掉");
Location m2(1,2);
m2 = g();
printf("匿名对象赋值给m2,匿名对象会被析构掉");
}
int main()
{
system("pause");
return 0;
}
默认构造函数
二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)默认拷贝构造函数
当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
总结:只要你写了构造函数,那么你必须用。
构造函数的初始化列表
#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");
}
#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); //产生新的匿名对象 //理解:当调用此构造函数时,会建立一个匿名对象,此对象被建立之后随着构造函数的结
} // 束而被析构掉,所以打印了析构函数的内容。此时,c所显示的就是乱码
~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()); //请问c的值是? 乱码
system("pause");
return 0;
}