C++ 类与对象(概念、构造、初始化)

发布于:2024-02-27 ⋅ 阅读:(86) ⋅ 点赞:(0)

C++类与对象(概念,构造,初始化)

学习要慢慢学,模块化学习,不要怕,不要急,太着急学习,反而囫囵吞枣,学了一遍之后反而不剩下什么,还要重新学。

慢就是快,快就是慢。

1. 类的概念

类的概念,类可以把它理解成一个抽象的概念,比如,我们可以看到有金毛犬、萨摩耶犬、中华田园犬等等,很多很多的类型,那么可不可以将它们抽离出都具有的概念呢?对,它们都是犬,那么犬就是一个类。

class Dog{

};

既然有了犬这个类,犬都有哪些东西呢?有名字(人起的),有品种(萨摩耶、金毛),有四条腿,有一张嘴,有两个耳朵,有一个尾巴等等,这些可以观测到的,这些属于犬吧,这些都是静态的,是完完全全属于犬的,那么这就是犬的属性,也就是我们所说的成员变量。犬会做什么?犬会走,会汪汪叫,会握手,会吃饭等等,这些属于动态的,这就是犬的动作,也就说我们所说的成员函数。那么就很明确了。类是若干对象的类似属性的抽离,先有对象,才有类(也是归类)。对象有属性,有动作。正好对应编程里面的对象的成员变量和成员函数。

这里的 public 和 private 就是一个修饰符,public表示公开,private表示私有(类内才能访问)。

class Dog {
public:
    void run();
    void bark();
    void eat();

private:
    char *_name;
    char *_type;
    int _age;
};

然后呢,通过Dog这个类,创建各种各样的具体的犬。

Dog dog;

这里直接通过 Dog 这个类,定义了 dog 这个对象变量,此时的 dog 就具备了可访问的 run、bark、eat 方法。

此时,我们就理解了什么叫做类、什么叫做对象,什么叫成员变量,什么叫成员函数。

2. 构造函数

那我们怎么定义犬的名字、类型、年龄呢?总不能直接修改吧,(private 不能直接修改),所以可以添加一些函数,用于修改。

void setName(char *name); 
void setAge(int age);
void setType(char *type); 

添加一些函数,用于获取。

char *getName();
int getAge();
char *getType(); 

这里只给出了函数的定义,函数的实现留在末尾,前边主要对概念进行梳理。(关注抽象)

每一次定义一个对象,都通过 set,set,set去定义属性,是不是有些麻烦,每次都要多写3行,当然麻烦,能不能直接定义对象的时候就把属性给赋值了?当然可以!通过构造函数。

什么叫做构造函数?可以理解成定义对象的时候直接给好属性,按照这个属性去生成。就不用创建好胚子之后,再对胚子进行上色什么的。我直接让胚子在炉里就上好颜色再出炉。(当然,我不太了解这行业,只是打个比方)

构造函数的形式:

类名(){

}

对,构造函数的形式就是类名加一个括号,加大括号(写函数体)而且没有返回值,比如上面的 Dog,它的构造函数就是:

class Dog{
public:
    Dog(){}
}

每个类都有构造函数,不管你写没写。那就出现疑问了,上面的明明没有写,却也能运行,创建出对象,为什么?

因为如果程序员没有手动写一个的话,系统会默认提供一个。当然,如果手动写了,那系统就不提供了。

构造函数因为有无参数也被分为有参和无参(这不是废话嘛),什么函数都是有参和无参。

构造函数,可以写很多很多,因为它满足函数重载。(没学过函数重载的,可以现在理解一下:函数重载,就是函数名相同,参数不同(参数类型不同、参数数量不同、参数位置不同)就会构成函数重载,只要满足这两项,它就是函数重载!别管那该死的返回值)

比如,我可以写成这样:

class Dog{
public:
	Dog(){}
	Dog(char* type, char* name, int age){}
	Dog(char* type, char* name){}
	Dog(char* type){}
	Dog(char* name, int age){}
}

注意,如果写 Dog(char* type, char* name) 和 Dog(char* name, char* type) 这两个,是错误的,因为两个参数类型一样,会让计算机产生二义性(小子,你到底想走哪个?)

3. 初始化列表

初始化列表是真正的初始化,写在构造函数大括号里的语句,二者之间反而有些差别。如:

Dog(char* type, char* name, int age){
    _type = new char[strlen(type) + 1]();
    _name = new char[strlen(name) + 1]();
    strcpy(_type, type);
    strcpy(_name, name);
    _age = age;                  
}

Dog(char* type, char* name, int age)
	:_type(new char[strlen(type)+1]())
	,_name(new char[strlen(name)+1]())
	,_age(age){
    strcpy(_type, type);
    strcpy(_name,name);
}

下面这种,就是初始化列表,直接写在 () 和 {} 中间的语句,以 : 开头,以 ,进行分隔。

二者的区别:

  • 使用初始化列表的,会初始化数据成员。
  • 函数体内定义的版本,在构造函数体内对数据成员赋值。

还有一点,使用初始化列表的时候,初始化的顺序按照数据成员定义的顺序进行。如果顺序不太对的话,有出错的可能。

比如:

class Point{
	Point(int x, int y)
		:_x(x)
		,_y(y){
		
		}
    
    int _x;
    int _y;
}

上面这个是对的,那么如果把 _y(y) 改成 _y(_x) 也是对的。如果先写 _y(y) 再写 _x(_y) 就错了。此时没有按照定义顺序进行初始化。

Point(int x, int y)
	:_y(y)
	,_x(_y)

好了,这篇文章就讲这三点,学习要循序渐进,模块化的进行学习,不要怕,慢慢学,都能学的会。代码在下面,

代码:

#include <iostream>
using std::cout;
using std::endl;

class Dog {
public:
    Dog() {
    }
    // 这里使用 const 是为了不让编译器报错。在C++里,纯字符串是 const char* 类型。
    Dog(const char *type, const char *name, int age) {
        _type = new char[strlen(type) + 1]();
        _name = new char[strlen(name) + 1]();
        _age = age;
    }

    void run() {
    }
    void bark() {
    }
    void eat() {
    }

    void setName(char *name) {
        // strlen 计算一个字符串的字符数量(不包含结束字符),因此需要 +1
        _name = new char[strlen(name) + 1]();
        strcpy(_name, name);
    }

    void setAge(int age) {
        _age = age;
    }

    void setType(char *type) {
        _type = new char[strlen(type) + 1]();
        strcpy(_type, type);
    }

    char *getName();
    int getAge();
    char *getType();

private:
    char *_name;
    char *_type;
    int _age;
};

int main(int argc, char *argv[]) {
    Dog dog_1;
    Dog dog_2("萨摩耶", "茂茂", 3);
    return 0;cc
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到