开个网站做代理,建什么网站赚钱,重庆专业网站推广,重庆玻璃制作厂家目录 1.基本函数
2.浅拷贝和深拷贝
3.初始化列表
4.const关键字的使用
5.静态成员变量和成员函数
6.C对象模型
7.友元
8.自动类型转换
9.继承 1.基本函数
#xff08;1#xff09;构造函数#xff0c;这个需要注意的就是我们如果使用类名加括号#xff0c;括号里面…目录 1.基本函数
2.浅拷贝和深拷贝
3.初始化列表
4.const关键字的使用
5.静态成员变量和成员函数
6.C对象模型
7.友元
8.自动类型转换
9.继承 1.基本函数
1构造函数这个需要注意的就是我们如果使用类名加括号括号里面加参数的方式这样的写法不是在调用构造函数而是在创建一个匿名对象
using namespace std;
class cgril
{
public:string m_name;int m_age;char m_memo[600];cgril(){initdata();cout 这里调用了cgril()构造函数 endl;}cgril(string name)//一个姓名参数的初始化函数{initdata();cout 调用了cgril(string name)构造函数 endl;m_name name;}cgril(int age)//一个年龄参数的初始化函数{initdata();cout 调用了构造函数 endl;m_age age;}cgril(string name, int age){initdata();cout 这里的调用了构造函数 endl;m_name name;m_age age;}void initdata(){m_name.clear();m_age 0;memset(m_memo, 0, sizeof(m_memo));}void show(){cout 姓名 m_name 年龄 m_age 备注 m_memo endl;}
};
2下面的两种写法看似一样实际上是截然不同的
第一行和23两行可以实现相同的效果但是这个第一行是属于显示的调用构造函数执行一次构造函数和析构函数
23行可以达到这个效果但是会执行两次构造函数和析构函数因为这个cgri(张三,20)执行的时候会生成一个临时对象赋值给这个gril对象相当于是赋值函数因为有这个临时对象的参与所以就会执行两次这个构造函数一次是构造gril这个对象一次是构造临时对象 3下面的就是两个拷贝构造函数第一个是没有重载的第二个是重载的
cgril(const cgril gg)
{m_name gg.m_name;m_age gg.m_age;cout 调用没有重载的拷贝构造函数 endl;
}
cgril(const cgril gg1,int ii)
{m_name gg1.m_name;m_age gg1.m_age - ii;cout 调用重载的拷贝构造函数 endl;
}
我们把这个没有重载的函数注释掉发现这个还是可以实现把g1的内容拷贝给g2的这个时候他没有去调用这个重载的函数因为这个重载的函数需要有2个参数但是我们调用的时候只传递一个参数 如果这个函数的第二个参数存在缺省参数编译器是会去调用的但是这个时候没有缺省参数依然可以实现拷贝这个就说明如果类里面定义了重载的拷贝构造函数却没有提供这个默认的拷贝构造函数这个时候编译器会自动提供拷贝构造函数
cgril g1;
g1.m_name 张三;
g1.m_age 18;
cgril g2(g1);
g2.show();
4编译器的优化问题就是返回值如果是一个类的时候可能会调用拷贝构造函数这个和变编译器有关有的编译器就会调用这个拷贝构造函数有的会把这个步骤给优化掉直接使用这个对象只不过给这个对象换了一个名字罢了
cgril func()
{cgril gg;gg.m_name 张三;gg.m_age 18;return gg;
}
int main()
{cgril ggg func();ggg.show();
}
2.浅拷贝和深拷贝
1我们这个编译器默认提供的拷贝构造函数就是一个浅拷贝如果指针p1指向一块空间拷贝给指针p2之后两个指针就会指向同一块空间释放一个空间另外的一个指针的指向就不存在了也就是说另外的一个指针就会变成野指针而且对于这个一个指针数据的修改同样会影响另外的一个指针数据
2深拷贝解决了浅拷贝的弊端就是如果想拷贝不是原来的那样直接指向同一块空间而是开辟出新的空间把这个原来的空间的内容给释放掉这样的话两个指针各自操控各自的区域就不会出现上面的野指针的问题了
3.初始化列表
1基本写法 这个初始化列表是跟在这个构造函数的后面的如果使用了初始化列表就不能再这个构造函数里面再次给这个成员变量赋值因为这个时候构造函数里面的赋值就会覆盖掉原来的初始化列表里面的初始值
2初始化列表里面的数值可以是具体的值也可以是这个参数表达式等等
cgril(string name, int age):m_name(name),m_age(age)
{cout 这里的调用了构造函数 endl;
}int main()
{cgril g1(李四, 18);g1.show();return 0;
}
这个是实例里面我们就是使用这个形参作为初始化列表里面的初始值
我们还可以在这个初始化列表的这个括号里面添加文字进行这个表达式的书写例如我们可以对于这个年龄进行加减操作对于这个名字的前面加上一些修饰语如下所示
cgril(string name, int age):m_name(高大威猛的 name), m_age(age 6)
{cout 这里的调用了构造函数 endl;
}
3当这个初始化列表的成员是类的时候使用初始化列表调用的是成员类的拷贝构造函数而赋值是先创建成员类的对象这个时候调用的是成员类的普通构造函数然后再赋值
下面的这段代码有助于我们了解两者之间的区别
class cboy
{
public:string m_xm;//男朋友的姓名cboy(){m_xm.clear();cout 调用cboy()普通构造函数 endl;}cboy(string name){m_xm name;cout 调用只有一个参数的cboy(string name)构造函数 endl;}cboy(const cboy bb){m_xm bb.m_xm;cout 调用了cboy(const cboy bb)拷贝构造函数 endl;}
};
class cgril
{
public:string m_name;int m_age;cboy m_boy;cgril(){cout 这里调用了cgril()构造函数 endl;}cgril(string name) //一个姓名参数的初始化函数{cout 调用了cgril(string name)构造函数 endl;m_name name;}cgril(int age) //一个年龄参数的初始化函数{cout 调用了cgril(int age)构造函数 endl;m_age age;}cgril(string name, int age, cboy boy):m_name(name), m_age(age){m_boy.m_xm boy.m_xm;cout 调用cgril(string name, int age, cboy boy)构造函数 endl;}void show(){cout 姓名 m_name 年龄 m_age,男朋友:m_boy.m_xm endl;}
};
int main()
{cboy boy(张三);cgril gg(李四, 18, boy);gg.show();return 0;
}
这个就是上面的代码的执行情况 执行主函数里面的第一行的时候就会调用cboy类里面的构造函数执行主函数里面的第二行的时候因为这个实参boy是一个对象传递给形参的时候使用的是值传递所以这个时候就会执行这个拷贝构造函数这个构造函数就是实参的内容传递给形参
用类创建对象的时候先去初始化构造函数的形参对象然后再去初始化类的成员控制台输出的第四行日志是cgril的对象gg创建的时候被调用的这个很好理解 当我们把这个boy形参改为引用传参而不是原来的值传参的时候这个时候就不会调用这个拷贝构造函数了因为这个引用的话使用的是和实现相同的东西只不过是找了一个别名罢了 上面介绍的所有内容这个超女的男朋友进行这个初始化列表的时候并没有走这个初始化列表相反这个是先执行构造函数在这个函数体里面进行初始化这个是两个步骤
下面我们使用初始化列表对于这个超女类的成员进行初始化操作 这个时候我们发现当我们使用初始化列表的时候调用的是成员类的拷贝构造函数这个时候对象的初始化和赋值是两步操作使用初始化列表对象的初始化和赋值就是一步操作
这个时候对比之前的没有使用初始化列表的情况显示对于这个形参执行的是普通构造函数然后在这个构造函数体里面进行赋值这个时候相信你就可以理解最开始的时候这句话的意思了 当这个初始化列表的成员是类的时候使用初始化列表调用的是成员类的拷贝构造函数而赋值是先创建成员类的对象这个时候调用的是成员类的普通构造函数然后再赋值 可见这个一个类的成员变量是一个类的时候这个初始化列表和普通的赋值初始化有以下的区别
第一就是这个调用的函数不一样赋值进行初始化时候调用的是子类的普通构造函数然后执行这个函数体里面的赋值语句对于这个成员变量进行赋值但是这个初始化列表的方法就是调用拷贝构造函数
第二点就是效率不一样普通的赋值初始化就是先创建初始化之后赋值但是这个初始化列表是初始化和赋值是一步就完成的请读者下去慢慢体会两者的区别
4当这个类的成员是常量或者是引用的时候只能使用初始化列表进行初始化操作
常量指的就是这个成员变量的前面加上const关键字进行修饰这个时候的成员变量必须在定义的时候进行初始化而我们的这个先构造再赋值就不符合这个条件只能使用初始化列表的方式进行初始化例如下面的这两种情况都需要使用初始化列表的方式进行初始化 4.const关键字的使用
1下面我们对于这个const的使用会在下面的这个代码基础上面进行修改和调整
class cgril
{
public:string m_name;int m_age;cgril(const string name, int age){m_name name;m_age age;cout 调用两个参数的cgril(const string name, int age)构造函数 endl;}void show() const{cout 姓名: m_name ,年龄: m_age endl;}
};int main()
{cgril g(张三, 18);g.show();return 0;
} 这个代码里面有一个show函数这个函数的作用就是打印输出这个名字和年龄的值我们是不会对于这个数值进行修改的这样的情况下我们就可以在这个函数的后面加上const进行修饰表示这个函数不会对于这个成员变量的值进行修改
这个时候我们如果在这个函数里面对于这个数值进行修改程序就会报错
2函数的相关调用
非const修饰的函数可以调用const修饰的和非const修饰的但是const修饰的函数只能调用const修饰的函数
同理我们对于这个函数可以使用const进行修饰对于这个对象我们也可以使用const进行修饰这样的对象叫做常对象常对象被这个const修饰因此常对象只能调用const修饰的函数不能调用非const修饰的函数
我们在运行的时候会发现这个会执行构造函数但是构造函数没有const修饰依然可以执行通过这个时候我们就可以理解为常对象调用const修饰的函数针对的是我们自己定义的函数这个构造函数编译器也会提供所以可以调用如果我们在这个构造函数后面加上const修饰反而程序会报错这个是因为在构造函数后面加上const是非法的
4.this指针
1我们通过下面的这段代码逐步过渡到thsi指针这个代码实现的就是两个超女的颜值比较然后输出颜值更高的我们使用yz这个整型变量表示颜值
class cgril
{
public:string m_name;int m_yz;//这个代表的是颜值我们使用整形数字代替1表示颜值最高3表示颜值最低;cgril(const string name, int yz){m_name name;m_yz yz;}void show() const{cout 我是 m_name ,颜值最高的超女 endl;}
};
const cgril pk(const cgril gg1, const cgril gg2)
{if (gg1.m_yz gg2.m_yz){return gg1;}else{return gg2;}
}
int main()
{cgril g1(张三, 1);cgril g2(李四, 2);cgril g3 pk(g1, g2);g3.show();return 0;
}
2这个代码是可以实现颜值比较的功能的但是他没有显示出来C的优势下面我们使用this指针实现以下这个功能
class cgril
{
public:string m_name;int m_yz;//这个代表的是颜值我们使用整形数字代替1表示颜值最高3表示颜值最低;cgril(const string name, int yz){m_name name;m_yz yz;}void show() const{cout 我是 m_name ,颜值最高的超女 endl;}const cgril pk(const cgril gg) const{if (gg.m_yz m_yz){return gg;}else{return *this;}}
};
int main()
{cgril g1(张三, 1);cgril g2(李四, 2);const cgril g3 g1.pk(g2);g3.show();return 0;
}
这个里面更符合C的编程逻辑就是使用thsi指针这个比较颜值的函数不再放在类的外面而是放在类的里面作为一个成员函数这样的成员函数实际上在传参的时候都传递过去了一个this指针只不过不会显示谁调用的成员函数这个this指针就会指向谁这个地方好像this指针的好处还显示不出来
我们上面是比较的两个超女的颜值因此这个C语言风格的比较判断还很简洁并不是很复杂但是当我们需要比较五个超女的颜值的时候可能这个C里面的thsi指针就会更具有优势 这个时候我们的成员函数不变但是这样写这个函数的调用就会很能凸显这个this指针的精妙了
这个时候我们的写法就可以使用这个指针只用一个式子就可以比较这五个的颜值
5.静态成员变量和成员函数
1静态成员变量就是在我们写的这个普通成员变量的前面加上const关键字普通的成员变量就变成了静态成员变量
class cgril
{
public:string m_name;static int m_age;cgril(const string name, int age){m_name name;m_age age;}void showname(){cout 姓名 m_name endl;}void showage(){cout 年龄 m_age endl;}
};
int cgril::m_age 0;
int main()
{cgril gg(张三, 18);gg.showname();gg.showage();return 0;
}
2上面的程序我们把这个m_age的成员变量定义为静态的成员变量这个时候我们在这个全局区里面必须对于这个静态的成员变量进行初始化需要指定这个作用域实际上就是类名对于这个普通的成员变量我们不能在这个主函数里面直接打印输出结果但是对于这个静态的成员变量我们可以加上他的作用域之后直接打印输出结果 这个时候我们就可以发现这个这个静态成员变量是可以直接打印的但是对于这个普通的成员变量如果我们直接打印就会报错这个是因为这个普通的成员变量必须使用创建得对象进行调用我们的静态成员变量是可以直接进行打印输出结果的
3关于静态成员函数
我们上面的静态成员变量可以直接使用不需要创建对象同理如果我们把这个函数设置成成员函数我们也是可以直接调用的对于普通的成员函数我们也是需要创建对象之后使用对象调用函数 关于这个静态成员变量是放在这个全局区进行定义初始化的所以这个成员变量的作用就和这个全局变量基本是一样的只有程序结束的时候这个成员变量才会被销毁 这个静态成员变量只有一份所以即使我们对于这个静态的成员变量多次进行赋值最后的时候这个成员变量的值只会保留一份但是普通的成员变量的值还是显示不同的但是这个静态成员变量的值只有一份所以打印结果显示的静态的成员变量都是相同的数值因为这个最新的20已经把前面的两个年龄全部覆盖掉了 4静态成员变量函数的访问权限
我们前面使用的这个是把年龄这个成员变量静态化函数也是把这个打印年龄的成员函数静态化这个时候我们在静态成员函数里面只能调用
另外这个静态成员函数没有this指针在非静态成员函数里面可以访问静态成员变量
私有的静态成员无法被类外面的成员访问这个想要验证的话也是很简单的我们把这个定义的静态的成员变量挪动到public的上面去这个时候没有范围修饰的静态成员变量的默认属性就是私有的这个时候我们如果在主函数里面对于这个成员变量进行调用就会报错这个也是静态成员变量区别于全局变量的重要原因
6.C对象模型
1这个存在一个叫做指针表通过这个指针表就可以找到对象成员的地址对象成员的地址在物理上面不一定是连续的但是这个指针表的指针一定是连续的并且这个指针表的大小是固定的通过这个指针表我们就可以找到这个类里面的成员的地址
2静态的成员变量属于类不属于对象所以这个静态的成员变量是独特一份的不会因为这个创建的对象的多少影响到静态的成员变量占用的内存空间的大小
3成员函数也是属于类的无论这个对象怎么变这个成员函数都是不受影响的它的大小也不会计算在这个对象里面他是独立于对象存储的
4成员函数的地址和成员变量的地址明显不在一起但是成员函数的地址和这个普通函数的地址是在一块的
5在我们程序员看来这个类里面的东西好像是完整的实际上这个成员变量和成员函数都是分散存储在这个内存里面的所以在这个C内部一定有一个东西用来记录这个成员的地址这个东西就是我们上面提及到的指针表通过这个指针表我们就可以找到这个成员变量和成员函数的地址
6如果成员函数里面没有用到成员变量这个时候我们可以使用空指针调用这个成员函数但是如果这个成员函数里面使用到了这个成员变量我们就需要进行判断这个指针是否为空 换言之通俗的讲就是这个我们在没有创建对象的时候调用函数当这个函数里面吗当这个函数里面没有使用到成员变量的时候我们是可以对于这个函数进行调用的
但是当这个函数里面使用了成员变量的时候而且没有创建对象我们使用这个成员变量就相当于这个使用空指针这个时候如果我们直接运行就会报错这个时候就需要对于这个指针进行判断是否是空指针
7对象的地址是第一个非静态成员变量的地址如果没有非静态的成员变量编译器会隐含的增加一个字节作为占位成员
7.友元
1友元实际上就是当我们没有办法访问某个私有成员变量的时候我们可以把这个函数设置为这个类的友元函数这样的话我们的函数就可以访问这个类的私有成员变量
using namespace std;
class cgril;
class cboy
{
public:void func1(const cgril g);void func2(const cgril gg);
};class cgril
{friend void cboy::func1(const cgril g);friend void cboy::func2(const cgril gg);
public:string m_name;void showname(){cout 姓名 m_name endl;}cgril(){m_name 张三;m_age 18;}
private:int m_age;void showage(){cout 年龄 m_age endl;}
};void cboy::func1(const cgril g) { cout g.m_name endl; }
void cboy::func2(const cgril gg) { cout gg.m_age endl; }int main()
{cgril g;cboy b;b.func1(g);b.func2(g);return 0;
}
2我们知道的都是对于一个函数使用自己的类里面的某个私有成员这个时候我们可以把这个函数放到类的里面作为友元函数这个函数的作用就是让我们可以使用这个类里面的私有成员
上面的是一个友元函数的情况涉及到两个类之间的相互调用这个时候就是函数的声明和定义过程比较繁琐这个时候我们在这个cboy的类里面就可以调用cgril类里面的东西了这个友元函数的作用就是我们可以调用其他类里面的函数
8.自动类型转换
1下面的就展示了几种自定义类型数据的转换方法例如这个显示转换隐式转换先创建对象再构建临时变量再去赋值
class cgril
{
public:int m_bh;string m_name;double m_weight;cgril(){m_bh 0;m_name.clear();m_weight 0;}void show(){cout 编号: m_bh 姓名: m_name 体重: endl;}cgril(int bh)//只有一个参数的构造函数{m_bh bh;m_name.clear();m_weight 0;}
};
int main()
{//常规写法cgril g1(8);//显示转换cgril g1 (cgril)8;;//隐式转换cgril g1 8;//创建对象创建临时对象再赋值cgril g1;g1 8;g1.show();return 0;
}
2这个自动类型转换不一定总是好的我们如果不想使用自动类型转换只需要在这个构造函数的前面添加exolcit关键字就会关闭编译器的自动类型转换功能
3转换函数这个函数就是把这个类转换为我们的内置数据类型例如下面的这个例子我们的类里面有string,int double三种数据类型我们使用这个转换函数之后就可以进行这个对应的转换operator 数据类型{}就是这个转换函数的基本格式
using namespace std;
class cgril
{
public:int m_bh;string m_name;double m_weight;cgril(){m_bh 6;m_name 张三;m_weight 66.6;}operator int() { return m_bh; }operator double() { return m_weight; }operator string() { return m_name; }
};
int main()
{cgril g;int a g;cout a a endl;string b g;cout b b endl;double c g;cout c c endl;return 0;
}
4实现转换的其他写法下面的两种写法也可以达到相同的效果 9.继承
1继承也叫做派生分为这个基类和派生类也叫做父类和子类表达的意思都是一样的只不过这个站的角度不一样 2为了更好地了解继承的相关语法我们构建下面的这个场景来进行相关的介绍我们首先定义了一个allcomers类用来表示的是这个选秀活动参加人员的名字和电话我们定义的第二个类表示的就是通过海选的超女这个时候我们这个通过海选的超女肯定也是需要这个名字和电话这些基本信息的我们就可以继承allcomers类的相关成员变量allcomers类就叫做父类cgril类就叫做子类
3继承就可以理解为子类可以获取这个父类的相关的成员变量和成员函数我们实例里面使用public作为这个继承的方式而且在这个子类里面我们是可以添加新的成员变量和成员函数的如果继承父类的成员函数和成员变量子类都会有的
class allcomers
{
public:string m_name;string m_tel;allcomers(){m_name 某某某;m_tel 不详;}void sing(){cout 我是一只小小鸟 endl;}void setname(const string name){m_name name;}void settel(const string tel){m_tel tel;}
};
class cgril:public allcomers
{
public:int m_bh;cgril(){m_bh 8;}void show(){cout m_bh m_name m_tel endl;}
};
int main()
{cgril g1;g1.show();return 0;
}
4继承的适用场景当新创建的类和原来的类有相同的成员变量和成员函数的时候我们就可以使用继承或者是类里面有很多的相似的地方的时候我们就可以把这个共性的东西给提取出来作为一个子类这样就可以简化我们的代码
例如这个各种各样的排序算法我们都需要输入数据输出数据只不过就是这个排序算法的原理不相同罢了这个时候我们就可以把这个对于数据处理的部分封装到一个子类里面去