C++类与对象(1)
类的设计:可以把属性和行为放在不同的权限下
struct和class区别在于某人的访问权限不同
- struct:默认共有
- class:默认私有
对象的初始化和清理
如果我们不写,系统会自己给我没写
- 构造函数的语法 类名(){}
没有返回值,也不写void
函数名和类型相同
可以有参,也可以无参
在调用对象会自动调用函数,无需手动调用,只调用一次
- 析构函数语法 ~类名(){}
同上,不过是无参(不可以重载),销毁时自动调用函数,只调用一次
构造函数的分类与调用
分类
- 有参和无参(默认)
- 普通和拷贝
拷贝函数
函数名(const 函数名 &p(对象)){
age = p.age;
}
#include //构造函数和析构函数
using namespace std;
class person{
public:
int age;
public:
//构造函数
//普通
person(){
cout << "Person无参构造函数的调用" << endl;
}
person(int a){
age = a;
cout << "Person有参构造函数的调用" << endl;
}
//拷贝
person(const person &p){
age = p.age;
cout << "Person拷贝构造函数的调用" << endl;
}
//析构函数
~person(){
cout << "Person析构函数的调用" << endl;
}
};
void test01(){
//调用
//1.括号法
//person p1;
//person p2(10);
//person p3(p2);
//注:调用默认构造函数时,不要加()(系统会以为是一个函数的声明)
//cout << "p2的年龄:" << p2.age << endl;
//cout << "p3的年龄:" << p3.age << endl;
//2.显示法
person p4;
person p5 = person(10);//右值:匿名对象,当前行结束,系统收回匿名对象
person p6 = person(p5);
//注:不要利用拷贝函数初始化匿名对象
//3.隐式转换法
person p7 = 10;//相当于person p5 = person(10);
person p8 = p7;
}
int main(){
test01();
return 0;
}*/
拷贝函构造函数的调用时机
c++中调用拷贝函数一般三种请况
- 使用一个已经创建完毕的对象来初始化一个对象
- 值传递的方法给函数参数传值
- 以值方式返回局部对象
#include //拷贝时机
using namespace std;
class Person{
private:
int m_Age;
public:
Person(){
cout << "person的默认无参构造函数调用" << endl;
}
Person(int age){
cout << "Person有参构造函数调用" << endl;
m_Age = age;
}
Person(const Person &p){
cout << "Person的拷贝调用" << endl;
m_Age =p.m_Age;
}
~Person(){
cout << "person的析构函数调用" << endl;
}
};
void test01(){
Person p1 = Person(20);
Person p2(p1);
}
void dowork(Person p){ }
void test02(){
Person p;
dowork(p);
}
Person dowork2(){
Person p1;
cout << (int*)&p1 << endl; //输出地址
return p1;
}
void test03(){
Person p = dowork2();
cout << (int*)&p << endl;
}
int main(){
test01();
cout << endl;
test02();
cout << endl;
test03();
return 0;
}
构造函数的调用规则
默认情况下,C++会至少给一个类添加3个函数
- 默认构造函数无参,函数体为空
- 默认析构函数无参,函数体为空
- 默认拷贝构造函数,对属性进行拷贝(所有的属性都进行赋值操作)
规则如下:
- 如果用户定义了有参构造函数,C++不在提供默认无参构造,但会提供默认的拷贝
- 如果用户定义了拷贝函数,C++不h会提供其他构造函数
注意:若只定义了拷贝(只有参同理),则
Person p1;
//和
Person p1(20);
//均是错误的(因为系统不会提供)
深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作(如果对其进行释放,则堆区的内存会重复释放,出现错误)
深拷贝:在堆区重新申请空间,进行拷贝操作
m_height = new int(*P.m_height); //new返回的值是地址
private:
int * m_height; //地址(指针)类型
初始化列表
作用:用来初始化属性
语法:
构造函数():属性1(值1),属性2(值2)...{}
来个浅例吧
#include
using namespace std;
class Person{
public:
//传统初始化
// Person(int a,int b,int c){
// m_A = a;
// m_B = b;
// m_C = c;
// }
//初始化列表
Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){} //参数a,b,c可以自由的改变所赋的值
int m_A;
int m_B;
int m_C;
};
void test01(){
Person p(30,20,10);
//Person p;
cout << "m_A:" << p.m_A << endl;
cout << "m_B:" << p.m_B << endl;
cout << "m_C:" << p.m_C << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果是30 20 10
类对象作为类的成员
C++类中的成员可以是另一个类的对象,我们成该成员为对象成员
例如:
class A{};
class B{
A a;
}
//敲黑板(好老的梗...)
//先构造A的对象(即先构造其他类的对象),再构造B的对象
//析构的顺序是相反的,先析构本类,再析构其他类
代码的简单例子
#include
using namespace std;
#include //要用字符串呢
class Phone{ //类一
public:
//品牌名字
string m_Pname;
Phone(string name){
m_Pname = name;
}
};
class Person{ //类二
public:
//姓名
string m_Name;
//手机
Phone m_Phone; //类一作为类二的成员
Person(string Name,string Pname):m_Name(Name),m_Phone(Pname){}
//相当于Phone m_Phone = Pname = Phone(Pname)(隐式转化)
};
void test01(){
Person p("张三","华为");
cout << p.m_Phone.m_Pname;
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果:华为
静态成员函数
静态成员变量就是加上const
静态成员分成:
- 静态成员变量
- 所有对象共享一份数据
- 再编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有对象共享一个函数
- 静态成员函数 只能访问静态成员变量(函数体内无法区分普通变量是那个对象的成员)
- 也是 有访问权限的,private下在类外就访问不到
#include
using namespace std;
class Person{
public:
//静态成员函数
void static func(){
m_a = 100;//静态成员函数访问静态成员变量
cout << "func的调用" << m_a << endl;
}
static int m_a;//类内声明类外初始化
};
int Person::m_a = 0;
//两种访问方式
void test01(){
//通过对象访问
Person p;
p.func();
//通过类名访问
Person::func();
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果
对象模型和this指针
成员变量和成员函数分开存储
- 只有非静态成员变量才属于类的对象上面
- 空对象占用 一个字节
C++编译器会给每个空对象分配一个字节的空间(独一无二的内存地址),防止区分空对象占内存的位置
#include
using namespace std;
class Person1{};
class Person2{
int m_a;//非静态成员变量,属于类的对象上
static int m_b;//静态成员变量,不属于类的对象上
void test01(){}//非静态成员函数,属于类的对象上
static void test02(){}//静态成员函数,不属于类的对象上
};
int Person2::m_b = 0;
void test01(){//空对象所占用的内存
Person1 p1;
cout << "p1 sizeof of p is " << sizeof(p1) << endl;
}
void test02(){//非空对象占用的内存
Person2 p2;
cout << "p2 sizeof of p is " << sizeof(p2) << endl;
}
int main(int argc, char** argv) {
test01();
test02();
return 0;
}
this指针
引子:在上面我们知道,非静态的成员函数只会生成一份函数实例,也是是说多个同类的对象会公用一块代码(一个函数),那么: 这一块代码是如何区分是那个对象调用自己呢?
通过this指针来解决上面的问题,this指针指向被调用的成员函数所属的对象(eg:p1调用就指向p1…)
- this指针是隐含在每一个非静态成员函数内的一种指针,不用定义,直接使用
用途
- 当形参和成员变量重名时,可用this来区分
- 在类的非静态成员函数返回对象本身(return *this;)
#include
using namespace std;
class Person1{//名称冲突
public:
Person1(int age,int age1){
this->age = age;
age1 = age1;
}
int age;
int age1;
Person1 & Add(Person1 &p){
this->age += p.age;
//this是一个指向p3的指针,*this就是对象p3的本体
return *this;
}
};
void test01(){
Person1 p1(18,18);
cout << "p1的年龄是" << p1.age << endl;
cout << "p1的年龄是" << p1.age1 << endl;
}
void test02(){//把p2的年龄加到p3上
Person1 p2(10,10);
Person1 p3(10,10);
p3.Add(p2).Add(p2);//链式编程思想
cout << "p3的年龄是" << p3.age << endl;
}
int main(int argc, char** argv) {
test01();
test02();
return 0;
}
结果
空指针访问成员函数
C++中允许空指针调用成员函数的,但是也要注意有没有用到this地址
如果用到this指针,则需要加以判断确保代码的健壮性
if(this == NULL) return;
看个小例子吧
#include
using namespace std;
class Person{
public:
void show(){
cout << "show的调用" << endl;
}
int m_age;
void get(){
if(this == NULL) return;
cout << "age=" << m_age << endl;
//默认this->m_age
}
};
void test01(){
Person * p = NULL;
//空指针可以访问成员
p->show();
p->get();
}
int main(int argc, char** argv) {
test01();
return 0;
}
const修饰成员函数
常函数
- 不可以修改成员属性
- 成员属性声明时+mutable关键字,在常函数中就可以修改了
class Person{
public:
//this本质 指针常量 Person * const this 指向不可以改变
//在成员函数后面+const const Person * const this,让指针指向的值不可以改变
void show() const{
m_a =100;//所以会报错哦
//其实是this->m_a = 100;
}
int m_a;
};
常对象
- 常对象 只能调用常函数
const Person p;
p.show();
友元
在程序里,有些私有的属性想让类外的特殊的一些函数或者类进行调用,就需要友元技术
作用(目的):让一个函数或是类访问另一个类中的私有成员
友元关键字: friend(友元:不是类的成员,不受访问限制)
友元的三种实现
- 全局函数友元
- 类做友元
- 成员函数做友元
全局函数做友元
#include
using namespace std;
#include
class Building{
//goodFriend是Building类的好朋友,可以访问啦
friend void goodFriend(Building &building);
public:
Building(){
SittingRoom = "客厅";
BedRoom = "卧室";
}
public:
string SittingRoom;//客厅
private:
string BedRoom;//卧室
};
//全局函数
void goodFriend(Building &building){
cout << "友元全局函数 正在访问:" << building.SittingRoom << endl;
cout << "友元全局函数 正在访问:" << building.BedRoom << endl;
}
void test01(){
Building building;
goodFriend(building);
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果
类做友元
大致流程:
- 先创建GoodFriend类的对象GF
- 调用本类下的构造函数:创建一个Building(同时调用Building的构造函数)
- 访问visit()函数,就可以访问building下的成员啦
#include
using namespace std;
#include
class Building{
//GoodFriend类是Building类的好朋友
friend class GoodFriend;
...//和上面一样
};
class GoodFriend{
public:
GoodFriend(){
//创建对象
building = new Building;
}
void visit(){//参观函数 访问Building中的属性
cout << "友元正在访问:" << building->SittingRoom << endl;
cout << "友元正在访问:" << building->BedRoom << endl;
}
Building * building;
};
void test01(){
GoodFriend GF;
GF.visit();
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果
成员函数做友元
流程与上面的几乎一样
#include
using namespace std;
#include
class Building;//防止在未创建BUilding类是报错
class GoodFriend{
public:
Building * building;
GoodFriend();
void visit();//参观函数 访问Building中的私有成员
};
class Building{
//visit()做为BUilding类的好朋友
friend void GoodFriend::visit();
public:
string SittingRoom;//客厅
private:
string BedRoom;//卧室
public:
Building();
};
//类外声明
Building::Building(){
SittingRoom = "客厅";
BedRoom = "卧室";
}
GoodFriend::GoodFriend(){
building = new Building;
}
void GoodFriend::visit(){//参观函数 访问Building中的私有成员
cout << "友元正在访问:" << building->SittingRoom << endl;
cout << "友元正在访问:" << building->BedRoom << endl;
}
void test01(){ //测试函数
GoodFriend GF;
GF.visit();
}
int main(int argc, char** argv) {
test01();
return 0;
}
结果:
运算符的重载
概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
对于内置的数据类型,系统知道如何进行运算
加号运算符重载(其他同理)
- 成员函数重载+号
本质:Person p3 = p1.operator+(p2);
#include
using namespace std;
class Person{
public:
int m_A;
int m_B;
/*======================================================*/
Person operator+(Person &p){
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
/*======================================================*/
};
void test01(){
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
cout << p3.m_A <
结果是两个20(相加成功)
- 全局函数重载+号
本质:Person p3 = operator+(p1,p2);
#include
using namespace std;
class Person{
public:
int m_A;
int m_B;
};
/*======================================================*/
Person operator+(Person &p1,Person &p2){
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
/*======================================================*/
int main(int argc, char** argv) { //函数和上面的一样
test01();
return 0;
}
结果也是两个20
注意:- 运算符的重载也可以发生函数重载(名字相同,参数不同)
- 不可以改变内置运算符
左移运算符的重载(<
Original: https://www.cnblogs.com/wht-de-bk/p/16171665.html
Author: T,a,o
Title: C++基础-类与对象(1)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570999/
转载文章受原作者版权保护。转载请注明原作者出处!