提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、封装的过程
1.定义
1.数据访问权限
类内:类的设计者编写代买的地方
类外:类的使用者编写代码的地方
访问权限关键字只限制类外,不限制类内。
(1)pubilc:允许类外访问
(2)privent
(3)protect
2.类对象内存空间
类中数据类型所占空间按c语言结构体对齐规则计算,函数名不占空间。如果类种不存在数据类型则,只有一个字节空间为了让使用则能够调用,预留出一字节的空间。
3.构造函数
调用时无需传参的构造函数称为默认构造函数
任意对象在创造时都会触发构造函数,构造函数没有返回值,名字与类名相同。目的是为了初始化类内成员。
在使用时如果调用默认构造,则可以不用写括号。
如果要传参则写括号写默认形参。
构造函数执行的过程:
(1)传参
(2)按照数据成员声明的数据类型为数据成员开辟内存空间
(3)执行构造函数函数体
初始化列表
初始化的顺序,与数据成员声明的数据相同,放在构造函数定义的地方。
分类:
按照参数分类,可以分为有参构造函数和无参构造函数(默认)
按照类型分类,可以分为普通构造函数和拷贝构造函数
#include<iostream>
using namespace std;
#include<string>
class Persion
{
private:
/* data */
public:
Persion() {/*默认构造函数*/
cout << "this no args structure fun" << endl;
}
Persion(int age) { /*复制构造函数*/
m_age = age;
cout << "this args structure fun" << endl;
}
Persion(const Persion &p) { /*必须穿传引用*/
m_age = p.m_age;
cout << "this copy structure fun" << endl;
}
~Persion();
int m_age;
};
// Persion::Persion(/* args */)
// {
// }
Persion::~Persion()
{
}
/* 调用 */
void test()
{
/*1.括号法*/
// Persion p1;
// Persion p2(18);
// Persion p3(p2);
// //注意事项:
// //不要在括号去调用默认构造函数去创建对象。编译器会认为是一个函数声明:
// Persion p4(); //错误的示范
// void test1(); //函数声明
/*2.显示法*/
// Persion p1;
// Persion p2 = Persion(10); //有参构造
// Persion p3 = Persion(p2); //拷贝构造
// Persion p3(p2);
//注意事项1
// Persion (10);//匿名对象 特点:当前执行后编译器会立即回收匿名对象
//注意事项2
// 不要利用拷贝构造函数创建匿名对象,Person (p3) = Person p3;
/*3.隐式转换法*/
Persion p4 = 4; //有参构造
Persion p5 = p4; //拷贝构造
}
int main()
{
test();
cout << "this main fun" << endl;
}
4.复制构造函数
复制构造函数一定是本类对象的常引用
使用范围/调用时机:
(1)使用已有的对象创建另一个对象
(2)函数值传递形参是类对象会触发复制构造函数
(3)类对象作为函数的返回值,会得到一个匿名函数。
#include<iostream>
using namespace std;
#include<string>
class Persion
{
private:
/* data */
public:
Persion() {/*默认构造函数*/
cout << "this no args structure fun" << endl;
}
Persion(int age) { /*复制构造函数*/
m_age = age;
cout << "this args structure fun" << endl;
}
Persion(const Persion &p) { /*必须穿传引用*/
m_age = p.m_age;
cout << "this copy structure fun" << endl;
}
~Persion() {
cout << "this destructure fun" << endl;
}
int m_age;
};
void do_work(Persion p) {
cout << "this is a test" << endl;
}
/*1.用一个已有的对象进行创建*/
void test01(void) {
Persion p1(10);
Persion p2(p1);
}
/*2.做函数参数*/
void test02(void) {
Persion p1;
do_work(p1);
}
Persion do_work2(void) {
cout << "this is a test" << endl;
Persion p1;
cout << (int*)&p1 <<endl;
return p1;
}
/*3.做函数返回值*/
void test03(void) {
// Persion p = test02();
Persion p2 = do_work2();
cout << (int*)&p2 <<endl;
}
int main()
{
// test();
/*1.用一个已有的对象进行创建*/
// test01();
// test02();.
test03();
// test04();
cout << "this main fun" << endl;
}
默认情况下,c++会给类添加至少三个函数
1.默认构造函数,函数体为空
2.默认析构函数,函数体为空
3.默认拷贝函数,对属性进行拷贝
构造函数调用规则如下:
1.如果用户定义了有参构造函数,c++不在提供默认构造函数,但是会提供拷贝构造函数
2.如果用户定义了拷贝构造函数,不会再提供其他构造函数
4.1深拷贝与浅拷贝
浅拷贝,带来的问题是堆区内存重复释放
如果属性有再堆区开辟的,一定要在自己提供的拷贝构造函数,防止浅拷贝带来的问题
#include<iostream>
using namespace std;
#include<string>
class Persion
{
private:
/* data */
public:
Persion() {/*默认构造函数*/
cout << "this no args structure fun" << endl;
}
Persion(int age,int height) { /*带参构造函数*/
m_age = age;
m_height = new int(height);
cout << "this args structure fun" << endl;
}
Persion(const Persion &p) { /*拷贝构造函数*//*必须穿传引用*/
m_age = p.m_age;
m_height = new int(*p.m_height);
cout << "this copy structure fun" << endl;
}
~Persion() {
if(!m_height) {
delete m_height;
}
cout << "this destructure fun" << endl;
}
int m_age;
int *m_height;
};
void do_work(Persion p) {
cout << "this is a test" << endl;
}
/*1.用一个已有的对象进行创建*/
void test01(void) {
Persion p1(10,160);
Persion p2(p1);
}
/*2.做函数参数*/
void test02(void) {
Persion p1;
do_work(p1);
}
int main()
{
test01();
cout << "this main fun" << endl;
}
4.2初始化列表
class Test
{
private:
/* data */
public:
// Test(/* args */);
~Test();
Test(int a, int b, int c):m_a(a),m_b(b),m_c(c){
}
int m_a;
int m_b;
int m_c;
};
Test::~Test()
{
}
int main()
{
Test a(1 , 2, 3);
cout << "a.m_a = "<<a.m_a<<endl;
cout << "a.m_b = "<<a.m_b<<endl;
cout << "a.m_c = "<<a.m_c<<endl;
cout << "this main fun" << endl;
}
当其他类对象作为本类函数成员的时候,构造时候先构造类对象,再构造自身,构造的顺序和析构的顺序相反
4.3静态成员
- 静态成员变量的特点
所有的对象共享一份数据
再编译阶段分配内存
类内申明,类外初始化
访问方式:
1.通过对象进行访问
2.通过类名进行访问
静态成员变量有访问权限
/*类的静态成员*/
class StaticTest
{
private:
/* data */
public:
StaticTest(/* args */);
~StaticTest();
static int m_a;
};
int StaticTest::m_a = 10;
StaticTest::StaticTest(/* args */)
{
}
StaticTest::~StaticTest()
{
}
void test_static_num(void){
cout <<StaticTest::m_a <<endl;
}
int main()
{
StaticTest T;
cout <<StaticTest::m_a <<endl;
T.m_a = 20;
cout <<T.m_a <<endl;
test_static_num();
}
- 静态成员函数
所有的对象共享一个函数
静态成员函数只能访问静态成员变量
5.析构函数
析构函数不允许带参数。
析构函 数执行流程:
1.执行析构函数函数体
2.按照数据成员声明的数据类型的逆顺序为数据成员开辟内存空间
6.C++对象模型和this指针
c++成员变量和成员函数是分开存储,
如果是一个类的类型是成员变量是空,内存大小是1字节,是编译器为了和其他类对象进行区分,创建的。
在类成员函数系统会默认传一个THIS指针
用途是:
1.解决名称冲突
2.返回对象本身用*this
class ThisPiont
{
private:
/* data */
public:
ThisPiont(/* args */);
ThisPiont(int num){
/*1.用于区别*/
this->num = num;
}
~ThisPiont();
ThisPiont& add_fun(ThisPiont p) {
this->num += p.num;
return *this;/*指向函数本身*/
}
int num;
};
ThisPiont::ThisPiont(/* args */)
{
}
ThisPiont::~ThisPiont()
{
}
int main()
{
ThisPiont p1(10);
ThisPiont p2(10);
// test.add_fun(test);
/*链式编程思想,类似与cout*/
p2.add_fun(p1).add_fun(p1).add_fun(p1);
cout << "p1 test.num" <<p1.num <<endl;
cout << "p2 test.num" <<p2.num <<endl;
}
6.1 this如果是空
class ThisPiont
{
private:
/* data */
public:
ThisPiont(/* args */);
ThisPiont(int num){
/*1.用于区别*/
this->num = num;
}
~ThisPiont();
ThisPiont& add_fun(ThisPiont p) {
this->num += p.num;
return *this;/*指向函数本身*/
}
void fun1(void) {
cout << "this fun1 " << endl;
}
void fun2(void) {
if (this == NULL) {
return;
}
cout << "this fun2" << num <<endl;
}
int num;
};
ThisPiont::ThisPiont(/* args */)
{
}
ThisPiont::~ThisPiont()
{
}
int main()
{
ThisPiont p1(10);
ThisPiont p2(10);
ThisPiont *test = NULL;
test->fun1();
test->fun2();
}
6.2 this 指针本质
是一个指针常量,指针指向不可以修改
类似与于 Person *const this;
6.3 常函数
class ThisPiont
{
private:
/* data */
public:
ThisPiont(/* args */);
ThisPiont(int num){
/*1.用于区别*/
this->num = num;
}
~ThisPiont();
ThisPiont& add_fun(ThisPiont p) {
this->num += p.num;
return *this;/*指向函数本身*/
}
/*常函数:在函数后面加const ,修饰的是this指针的指向,让指针指向的值也不可以修改*/
void showPont(void) const
{
// this->num = 100;/*error*/
this->numtest = 100;
}
void fun() {
}
int num;
mutable int numtest;/*在常函数中,加入mutable 可以被修改*/
};
ThisPiont::ThisPiont(/* args */)
{
}
ThisPiont::~ThisPiont()
{
}
void test03(void) {
const ThisPiont test;
test.showPont();/*常对象只能调用常函数*/
//test.fun();/*error*/
}
int main()
{
test03();
}
6.3友元函数
共有以下几类:
1.全局函数:将函数的声明在类内,并且在前面加一个friend,这样全局函数就可以访问,类内的私有成员
friend void test(void);
2.类做友元:将类的声明在类内,并且在前面加一个friend,另一个类就可以访问,类内的私有成员
friend class TEST;
3.成员函数:将类的成员函数的声明在类内,并且在前面加一个friend,这样全局函数就可以访问,类内的私有成员
TEST 类下面的test函数可以访问声明了类的私有成员
friend TEST::void test(void);
7.类的组合
如果某个类当中的数据成员要其带参构造构造时,使用初始化列表向其传参。
8.系统提供的类函数
1.默认构造函数
2.默认复制构造函数
3.默认析构函数
4.默认复制运算符重载函数
5.非常对象取地址运算符重载
6.常对象取地址运算符重载
7.移动复制运算符
8.移动复制构造函数