C++ 面向对象基础

概述

写C的时候,我们是面向过程的,而在C++中,我们则是面向对象,面向对象的思想可谓是一个革新,这也是使得构建缤纷的代码世界变得更加容易。本文中我想联系生活中的实际来说说和面向对象有关的事情。在此感谢阅读。

new和malloc

在C中,我们使用malloc来为指针分配空间,并且可以自己去操控这一片空间,C++中提供了类似的new操作符来达到和malloc函数同样的效果

  1. 分配一个int的空间
    int* p = new int(10);//c++,这里面的10表示*p=10,而不是10个int空间
    delete p ;//释放空间
    
    等价的C操作如下
    int* p = (int*)malloc(sizeof(int));//c
    *p = 10 ;
    free(p)
    
  2. 分配10个int空间
    int* p = new int[10];//c++,这里面的10表示10个空间,里面的内容没有被初始化
    delete[] p;//注意是delete[]
    
    //等价的C操作如下
    int* p = (int*)malloc(10*sizeof(int));//c
    free(p);
    

引用

看下面的程序

void swap(int& value1,int& value2){
	value1=19;
	value2=90;
}

int main(){
	int a = 0 ;
	int b = 0 ;
	swap(a,b);//a,b可以被改变
	return 0;
}

其实本质是这样的

void swap(int* const value1,int* const value2){
	*value1=19;
	*value2=90;
}

int main(){
	int a = 0 ;
	int b = 0 ;
	swap(&a,&b);//a,b可以被改变
	return 0;
}

我们从中可以看到,引用的本质就是一个常量指针,或者说引用是为一个变量起了一个别名。由于是常量指针,所以引用的两个性质可以得到合理解释:

  1. 引用必须要初始化:假设引用没有初始化,那么它将指向内存中一块未知的区域,而引用又是常量指针,所以我们无法修改其值,此时对引用的操作是危险的

  2. 引用不可以更改值:因为引用是常量指针


引用的赋值和常量引用

有一个操作类似更改引用,但其实是引用的赋值,我们来看一下

	int a = 10 ;
	int b = 20 ;
	int& c = a;
	c =b ;

在内存中的行为其实是这样的
在这里插入图片描述

c=b并不是意味着c现在是b的别名,而是表示 a的引用c 把b的值 赋给了 a
我们在上文提到了,引用是常量指针,比如

int a = 20;
int& c = a;

等价于

int a = 20;
int* const c = &a;

引用让我们拥有了修改a的权限,但是如果我们只希望具有对a的读权限,而希望禁止写a,我们可以使用常量引用

int a = 20 ;
const int& b = a ;
//a = 30 该操作是禁止的
//常量引用等价于 const int* const b = &a;

综上

引用指针
int a =20 ; int& b = a;int a =20 ; const int* b = &a;
int a =20 ; const int& b = a;int a =20 ; const int* const b = &a;

函数

  1. 函数的默认参数

    void fun(int a =20){
        cout<<a<<endl;
    }
     int main() {
         fun();
    }
    

    该操作是被允许的

  2. 函数重载

    int add(int a ,int b){
    	return a+b ;
    }
    double add(double a ,double b){
    	return a+b ;
    }
    
    

封装

说到封装,也就必须要提到:

1. 面向对象的基本概念,包括类,对象,属性,方法等
2. 权限修饰符,包括 public protected private
3. get set方法

首先我们来说说类,对象,属性,方法

1. 对象是一个实体,而类,是将该实体的特征抽象出来的一个模板
2. 我们可以通过模板(类)来创建实体(对象),这又称为类的实例化
3. 属性是抽象出来的静态特征,方法是抽象出来的动态特征

比如以动物为例子,🐶类是一个类,是一类动物的总称,🐶类的属性有毛的颜色,腿长,体温等。🐶类的方法有比如汪汪叫,吃饭等。而实实在在存在的狗类的一个实体便是🐶类的一个实例化(对象),比如宠物店的每一只狗,外婆家养的小黑等。现在我们用代码去描述一下上面所说


class Dogs{
public:
    string hairColor;
    float lengthOfLegs;
    float temprature ;
    void Barking(){
        cout<<"wang wang wang"<<endl;
    }
    void Eat(){
        cout<<"Dogs can eat thing"<<endl;
    }
};

 int main() {
    Dogs smallBlack;
    smallBlack.hairColor="black";
    smallBlack.lengthOfLegs=40;
    smallBlack.temprature=36.3;
    smallBlack.Barking();
    smallBlack.Eat();
    cout<<"smallblack have "<<smallBlack.hairColor<< " hairs"<<endl;
    cout<<"smallblack's legs length is "<<smallBlack.lengthOfLegs<< " cm"<<endl;
    cout<<"smallblack's temperature is "<<smallBlack.temprature<< " centigrade"<<endl;
     return 0;
 }

大家可能注意到了在定义Dogs类的时候,我们已经使用了权限修饰符,且看如下

修饰符权限
public所有位置都可以访问
protected只有类和子类中可以访问
private只有本类中可以访问
类在创建的时候的缺省权限是private,这也是为什么我们需要显式地定义public

构造函数,析构函数

这两个函数的英文名分别是constructordestructor

构造函数析构函数
实例化时进行对象的初始化在对象被销毁之前进行一些操作
可以重载不可重载
由系统调用由系统调用
无返回值无返回值
可以有输入参数无输入参数
函数名为类名函数名为~类名

比如在上面的Dogs类里面写如构造函数和析构函数如下(主函数没有变)


class Dogs{
public:
    Dogs(){
        cout<<"===this is constructor==="<<endl;
    }
    ~Dogs(){
        cout<<"===dog dispeared==="<<endl;
    }
    string hairColor;
    float lengthOfLegs;
    float temprature ;
    void Barking(){
        cout<<"wang wang wang"<<endl;
    }
    void Eat(){
        cout<<"Dogs can eat thing"<<endl;
    }
};

输出为

===this is constructor===
wang wang wang
Dogs can eat thing
smallblack have black hairs
smallblack's legs length is 40 cm
smallblack's temperature is 36.3 centigrade
===dog dispeared===

通过此可以看到构造函数和析构函数是什么时候被调用的,调用的具体格式和语法我就不说了

匿名对象和拷贝构造


class Person{
public:
    int age=0 ;
    Person(){
        cout<<"this is constructor and age is "<<age<<endl;
    }
    Person(int inputage){
        age=inputage;
        cout<<"this is constructor and age is "<<age<<endl;
    }
    Person(const Person& p){
        age=p.age;
        cout<<"this is copy constructor and age is "<<age<<endl;
    }

    ~Person(){
        cout<<"this is destructor"<<endl;
    }

};
  1. 匿名对象
    如果我们使用People(10)作为一条语句而不使用任何变量来接收它,那么我们就是创建的一个匿名对象,该对象将会在此语句执行之后被销毁


  2. 拷贝构造的应用场景一般有三种

    应用场景类比
    使用创建好的对象来初始化另一个对象变量赋值
    值传递给函数传参变量作为函数输入参数
    函数返回值变量作为函数返回值
  3. 拷贝构造:使用已经创建好的对象来初始化另一个对象

    People p1(10);//创建一个对象,传递值10
    People p2(p1);//通过已经创建好的对象p1来初始化p2
    

    第二个语句调用的构造函数是Person(const Person& p),该构造函数的输入参数是一个引用,该函数就是拷贝构造函数,而且输入参数是一个常量引用,所以我们对输入参数的权限只有读。这两条语句的详细行为如下:

    1. 调用构造函数 Person(int inputage)创建对象p1,通过传递参数,age为10
    2. 调用拷贝构造函数Person(const Person& p),通过引用传递来传递p1,此时p是p1的别名,由于是常量引用,无法通过p来修改p1的值,对p1只有读权限。
    3. 创建对象p2,通过引用p来把p1的参数赋值给p2,注意在此过程中我们是通过引用传递。
      在这里插入图片描述

3.拷贝构造:值传递给函数传参

void pass(const Person& person){
		    Person p2 = person;
		}
		
		int main() {
			Person p1(10);
			pass(p1);
		    return 0;
		 }

在主函数中发生的行为如下

  • Person p1(10);调用Person(int inputage)创建对象,初始化值 为10
  • 调用函数pass(const Person& person),在函数传递参数的过程中,现在personp1的别名
  • 调用Person(const Person& p)来构造对象p2,现在pperson的引用,p2的属性的初始化是通过p=>person=>p1来进行传递的
  • pass函数调用结束

  1. 拷贝构造:函数返回

    Person returnPerson(){
        Person person1(10);
        return person1;
    }
    
     int main() {
        Person person2 = returnPerson();
        cout<<person2.age<<endl;
        return 0;
     }
    
    

    程序行为:

    • 调用构造函数Person(int inputage)创建对象person1
    • 返回的时候调用Person(const Person& p)来创建对象person2

浅拷贝和深拷贝

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值