[学习笔记-C++篇]day6 类&对象

为了保证内容完整性,将day4的类搬运一下。
感谢大佬几款优秀的支持C、C++在线编译器


stage1——6天入门阶段

教程网站:C++ 教程
在线编译器:compile c++ gcc online
刷题网站:阶段1第一关:基本数据类型

day6 planA
教程2(1),刷题3(3)
教程完成度100%,刷题完成度66.6%
主要原因:其实今天有点怠惰了…基础知识影响速度


1.类&对象

类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据、方法、函数称为类的成员。

1.1 类的定义

它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
在这里插入图片描述
注意:classstruct定义时,{}后一定要加;

class cloth
{
public:
  int num;
  char color[10];
};

int  main()
{
  cloth c;
  return 0;
}

在类对象作用域内,public公共成员在类的外部是可访问的
对象是类的实例,对类进行定义后就获得了一个对象。

#include <iostream>

using namespace std;

class cloth
{
    public:
    int num;
    int types;
    char brand[20];
    int sum()//可以直接在class里面定义,也可以在外面定义,但要在主函数前面
    {
        return num*types;
    }
    void get(int n, int t);
};

void cloth::get(int n, int t)
{
    num=n;
    types=t;
}

int main()
{
    cloth c;
    
    //c.num=5;
    //c.types=2;
    
    c.get(5,2);
    int output=c.sum();
    
    cout << "the sum of cloth is " << output << endl;
    
    return 0;
}

梳理一下写的时候会遗漏的东西:

1)class中要注意写上public:private:protected:
2)如果class中的函数成员要在类之外定义,那么一定要加上范围解析运算符,比如cloth::
3)给class中成员赋值或者调用函数的时候,记得使用类名.,如c.这样的格式去调用成员

但是私有的成员和受保护的成员不能使用直接成员访问运算符.来直接访问

1.2 类成员函数

类的成员函数,就是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样,是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员

成员函数可以定义在类定义内部,或者单独使用范围解析运算符::来定义,在::运算符之前必须使用类名。

#include <iostream>
#include <cmath>

using namespace std;

class box
{
    public:
     int a;
     int b;
     int get(int c);
     void put(int d)
     {
         cout << d << endl;
     }
};

int box::get(int c)
{
    return pow(c,3)+a+b;
}

int main()
{
    box bbox;
    bbox.a=1;
    bbox.b=2;
    int output=bbox.get(3);
    
    bbox.put(output);
    
    return 0;
}

1.3 类访问修饰符

数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记publicprivateprotected来指定的。关键字publicprivateprotected称为访问修饰符。

成员和类的默认访问修饰符是private
每种修饰符后可以定义多个成员,不需要{},直接指令就可以。

  1. public

公有成员在程序中类的外部是可访问的

  1. private

private变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员

#include <iostream>
#include <cmath>

using namespace std;

class box
{
    private:
     int p;
    
    public:
     int a;
     int b;
     int get(int c);
     void put(int d)
     {
         cout << d << endl;
         cout << p << endl;
     }
     void set(int s)
     {
         p=s;
     }
};

int box::get(int c)
{
    return pow(c,3)+a+b;
}

int main()
{
    box bbox;
    bbox.a=1;
    bbox.b=2;
    int output=bbox.get(3);
    bbox.set(4);//就是只能通过在类中定义函数对private成员进行定义,不能直接用bbox.p=4;这样的指令
    
    bbox.put(output);
    
    return 0;
}

友元函数具体介绍看1.8

  1. protected
    protected成员在类、友元函数和派生类(即子类)中是可访问的。
#include <iostream>

using namespace std;

#if 1
class box
{
    protected:
     int a;
};

class pbox:box//定义派生类
{
    public:
     void set(int b)
     {
         a=b;
     }
     int change();
};

int pbox::change()
{
    return a+1;
}

int main()
{
    pbox bbox;
    bbox.set(5);
    int output=bbox.change();
    cout << output << endl;
    
    return 0;
}
#endif

1.4 继承中的特点

派生类继承基类中的成员之后,由于继承方式的不同,会相应改变成员所属的性质:

1.public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private

2.protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private

3.private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private
整理一下,大概就是:public < protected < private的限制性,因此public继承不改变任何属性,protected继承将public变为protectedprivate继承将所有类型变为private

但无论哪种继承方式,上面两点都没有改变:

1.private成员只能被本类成员(类内)和友元访问,不能被派生类访问;
2.protected成员可以被派生类访问。

  1. public继承
#include <iostream>

using namespace std;

class box
{
    public:
    int a;
    box()
    {
        a=1;
        b=2;
        c=3;
    }
    void output();
    protected:
    int b;
    private:
    int c;  
};

void box::output()
{
    cout << a << " " << b << " " << c << endl;
}

class  pbox: public box//定义派生类
{
    public:
    int d;
    pbox(int i)
    {
        box();
        d=i;
    }
     void output()
     {
         cout << a << " " << b << " " << d << endl;
     }
    
};

int main()
{
    pbox p(4);
    p.output();
    
    return 0;
}
  1. protected继承
    注意下面在B中继承A里的a1会从public变为protected,所以在派生类外不可访问了。
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
  int a;
  A(){
    a1 = 1;
    a2 = 2;
    a3 = 3;
    a = 4;
  }
  void fun(){
    cout << a << endl;    //正确
    cout << a1 << endl;   //正确
    cout << a2 << endl;   //正确
    cout << a3 << endl;   //正确
  }
public:
  int a1;
protected:
  int a2;
private:
  int a3;
};
class B : protected A{
public:
  int a;
  B(int i){
    A();
    a = i;
  }
  void fun(){
    cout << a << endl;       //正确,public成员。
    cout << a1 << endl;       //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
    cout << a2 << endl;       //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
    cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
  }
};
int main(){
  B b(10);
  cout << b.a << endl;       //正确。public成员
  cout << b.a1 << endl;      //错误,protected成员不能在类外访问。
  cout << b.a2 << endl;      //错误,protected成员不能在类外访问。
  cout << b.a3 << endl;      //错误,private成员不能在类外访问。
  system("pause");
  return 0;
}
  1. private继承
    注意下面在B中继承A里的a1会从public变为privatea2protected变为private,所以不可再被派生访问了,但在继承的派生类中可以访问,也不能在类外访问。
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
  int a;
  A(){
    a1 = 1;
    a2 = 2;
    a3 = 3;
    a = 4;
  }
  void fun(){
    cout << a << endl;    //正确
    cout << a1 << endl;   //正确
    cout << a2 << endl;   //正确
    cout << a3 << endl;   //正确
  }
public:
  int a1;
protected:
  int a2;
private:
  int a3;
};
class B : private A{
public:
  int a;
  B(int i){
    A();
    a = i;
  }
  void fun(){
    cout << a << endl;       //正确,public成员。
    cout << a1 << endl;       //正确,基类public成员,在派生类中变成了private,可以被派生类访问。
    cout << a2 << endl;       //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。
    cout << a3 << endl;       //错误,基类的private成员不能被派生类访问。
  }
};
int main(){
  B b(10);
  cout << b.a << endl;       //正确。public成员
  cout << b.a1 << endl;      //错误,private成员不能在类外访问。
  cout << b.a2 << endl;      //错误, private成员不能在类外访问。
  cout << b.a3 << endl;      //错误,private成员不能在类外访问。
  system("pause");
  return 0;
}

1.5 类构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。

就像上面程序里的A(){}B(int a){}这样。

#include <iostream>

using namespace std;

class box
{
    public:

    box(int i)
    {
       cout << "the program's starting." << endl;
       a=i;
    }
   
    void set()
    {
        cout << "a=" << a << endl;
    }
    
    private:
    int a;  
};

int main()
{
    box b(6);
    b.set();
    
    return 0;
}
输出-----------------
the program's starting.
a=6

也可以设置什么参数都不用加的构造函数:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      Line();  // 这是构造函数
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
 
// 程序的主函数
int main( )
{
   Line line;
 
   return 0;
}

使用初始化列表来初始化字段

Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}

//等于
Line::Line(double len)
{
  length=len;
  cout << "Object is being created, length = " << len << endl;
}

多个字段初始化:

Line::Line( double len,double wid, double hei): length(len),width(wid),height(hei)
{
    cout << "Object is being created, length = " << len << endl;
}

1.6 析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号~作为前缀,它不会返回任何值,也不能带有任何参数。
析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

#include <iostream>

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      Line();  // 这是构造函数
      ~Line(); //析构函数
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created." << endl;
}
 
Line::~Line()
{
    cout << "Object is over." << endl;
}

// 程序的主函数
int main( )
{
   Line line;
    
    int a=1,b=2;
    cout << a+b << endl;
 
   return 0;
}
输出-------------
Object is being created.
3
Object is over.

1.7 拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。

  1. 通过使用另一个同类型的对象来初始化新创建的对象。
  2. 复制对象把它作为参数传递给函数。
  3. 复制对象,并从函数返回这个对象。

如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

classname (const classname &obj) {
   // 构造函数的主体
}

参考调用拷贝构造函数的三种情况拷贝构造函数在哪几种情况下会被调用

1.直接用另一个类的对象初始化
eg. 
Line line;
Line cp(line);
Line cy=line;

2.函数形参是类的对象(必须是值传递,比如下面的例子 void display(Line obj),引用传递不会)

3.函数返回值是类的对象或引用
eg.
Line get(int a){}
Line &get(int a){}
#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数
 
   private:
      int *ptr;
};
 
// 成员函数定义,包括构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}
 
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}
 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函数
int main( )
{
   Line line(10);
 
   display(line);
    
    Line l(line);//或者Line l=line;
    display(l);
 
   return 0;
}
输出------------------------
调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
调用拷贝构造函数并为指针 ptr 分配内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存
释放内存

ps. 疑问:问什么明明在主函数中没有命令使用拷贝构造函数时,也会调用拷贝构造函数呢???

1.8 友元函数

类的友元函数是定义在类外部,但有权访问类的所有私有private成员和保护protected成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );//正常声明函数,然后前面加上friend
   friend class boxer;//将boxer的所有成员函数作为box的友元
   void setWidth( double wid );
};

友元函数其实可以看做一个独立的子函数,只是可以访问类中的成员变量,需要在类中声明(用friend)。
注意:
1)在类中声明,用friend
2)在class外部定义,不需要加类名和::符号;
3)可以访问class中任何成员,函数形参为类名+类形参
4)访问类成员的时候和主函数一样,要用类名.
5)调用的时候不需要加类名.

#include <iostream>

using namespace std;

class box
{
    public:
     int a;
     
     friend void put(box b);
};

void put(box b)
{
     cout << b.a << endl;
}

int main()
{
    box bbox;
    bbox.a=1;
    put(bbox);
    
    return 0;
}

1.9 内联函数

C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。

在函数名前面放置关键字inline,如果已定义的函数多于一行,编译器会忽略inline限定符。
在类定义中的定义的函数都是内联函数,即使没有使用inline说明符。

需要注意2点:
1)在内联的函数定义前加上inline,比如inline void get() {cout << "hello world!" << endl;}
2)一般只有一句指令,否则编译器忽视掉inline

1.10 this指针

在指针的部分提及过,主要是使用lambda函数[]()->type{}的时候,如果用了this指针,就要显式传入。

在 C++ 中,每一个对象都能通过this指针来访问自己的地址。this指针是所有成员函数的隐含参数(友元函数没有)。因此,在成员函数内部,它可以用来指向调用对象。

#include <iostream>
#include <cmath>
 
using namespace std;
 
class Line
{
   public:
      Line(int b)             // 简单的构造函数
      {
          a=b;
      }
    int con()
    {
        return pow(a,2);
    }
        
    int cmp(Line L)
    {
        return this->con() > L.con();
    }

   private:
      int a;
};
 
// 程序的主函数
int main( )
{
    Line l1(3);
    Line l2(5);
    
    if(l1.cmp(l2))
        cout << "l1 > l2." << endl;
    else
        cout << "l1 < l2." << endl;
 
    return 0;
}

注意:
1)在类的函数中使用this指针,通常是要调用另一个类中的成员,因此函数形参为
2)使用this要和->相结合来调用本类里成员,指向当前类的某个成员

1.11 指向类的指针

和指向结构体的指针一样。

注意:
1)使用之前需要定义,比如class box{}; int main(){ box *b; return 0;}
2)赋值要结合&符号,比如box c; b=&c;
2)要用->访问成员,比如b->num;

//搬运
#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   Box *ptrBox;                // Declare pointer to a class.

   // 保存第一个对象的地址
   ptrBox = &Box1;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;

   // 保存第二个对象的地址
   ptrBox = &Box2;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;
  
   return 0;
}

1.12 类的静态成员

使用static关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。就是值是统一的。

如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。
注意!!:不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符::来重新声明静态变量从而对它进行初始化
在这里插入图片描述

#include <iostream>
#include <cmath>
 
using namespace std;
 
class Line
{
   public:
    static int s;
      Line(int b)             // 简单的构造函数
      {
          a=b;
      }
    int con()
    {
        return pow(a,s);
    }
    
   private:
      int a;
};
 
int Line::s=5;

// 程序的主函数
int main( )
{
    Line l(3);
    cout << l.con() << endl;
    
 
    return 0;
}

2.继承

继承允许我们依据另一个来定义一个类,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类

继承代表了is a关系。

// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};


//派生类
class Dog : public Animal {//这里就声明了public继承
    // bark() 函数
};

使用派生类的注意事项:
1)派生类需要结合:符号使用;
2)派生类自己的定义和基类是一样的,比如函数在类外定义要用派生类名::
3)派生类只有public才可以调用基类的protected,所以一定记得加上public
4)在派生类中可以修改基类的protected成员的值。

继承时不显示声明时,则默认是private继承。在struct中默认public继承。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值