C++基础知识(Ⅱ)

前言

此部分主要对C++类和对象的编程部分进行巩固。
参考资料源于:http://c.biancheng.net/cplus/

一.类的定义

1.类的定义

类是用户自定义的类型。一个类中包含成员变量和成员函数。
一个简单的类定义如下:

class Student{
public:
    //成员变量
    char* name;
    int age;
    //成员函数
    void information(){
        cout<<"姓名"<<name<<'\n'<<"年龄"<<age<<endl;
    }
};  //注意此处有个分号,表示类定义结束

2.创建对象

定义完类后,可创建对象。可用点号’.'来访问成员变量和成员函数。

int main(){
    Student students;  //创建对象名为students
    students.name = "Mr.Right";
    students.age = 28;
    students.information();
    return 0;
}

3.使用对象指针

有两种方法,一种直接给对象指针赋值,一种给对象指针创建新对象。

//method 1
Student students;
Student* pstudents = &students;

//method 2
Student* pstudents = new Student;

通常使用method 2来创建新对象指针,此时需要用指针命令来访问类成员。

int main(){
    Student* pstudents = new Student;
    pstudents -> name = "Mr.Right";
    pstudents -> age = 28;
    pstudents -> information();
    delete pstudents;
    return 0;
}

4.成员函数在类外的定义

成员函数必须现在类内声明,可在类内或类外定义,在类内定义的成员函数默认为内联函数(inline),而在类外定义的不是内联函数,除非在外部定义时加上inline使其变成内联。
内联函数是将代码复制到调用的地方,能够减少因函数调用引起的额外开销(参数压栈、寄存器保存与恢复等),但也会增加代码大小。
一个合理的经验准则是,对函数体较小的进行内联,不要内联一个相当大的函数。
通常我们开发中采取的写法是将非内联函数放在类内声明,类外定义。将内联函数放在类内定义。
在外部定义的函数用域符号’::'来表示。

class Student{
public:
    char* name;
    int age;
    
    //默认内联函数定义
    void information(){
        cout<<"姓名"<<name<<'\n'<<"年龄"<<age<<endl;
    }
    void information2();  //函数声明
};

void Student::information2(){
    cout<<"姓名"<<name<<'\n'<<"年龄"<<age<<endl;
}

5.类成员的访问权限

符号含义
public可以被该类中的函数、子类的函数、友元函数访问,也可以由该类的对象访问
protected可以被该类中的函数、子类的函数、友元函数访问,但不可以由该类的对象访问
private可以被该类中的函数、友元函数访问,但不可以由子类的函数、该类的对象访问

成员变量大都以m_开头,这是约定成俗的写法,不是语法规定的内容。以m_开头既可以一眼看出这是成员变量,又可以和成员函数中的形参名字区分开。

二.类的成员函数

1.构造函数

构造函数是一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户显式调用(也无法调用),而是在创建对象时强制自动执行。

class Student{
private:
    char* m_name;
    int m_age;
    float m_score;
public:
    Student(char* name, int age, float score);  //构造函数
    void information2();  //普通成员函数
};

//定义构造函数,实质为重载了构造函数,下篇会讲到
Student::Student(char* name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}

//定义普通成员函数
void Student::information2(){
    cout<<"姓名"<<name<<'\n'<<"年龄"<<age<<endl;
}

2.构造函数的重载

定义一个类,会自动默认生成一个空构造函数,我们通常需要重载构造函数以达到我们的目的。
注意:我们一旦重载了构造函数,系统就不会自动生成空的构造函数,如果我们需要空构造函数,可以再重载一个。
以下代码源自:http://c.biancheng.net/view/2221.html,笔者稍加改动。

#include<iostream>
using namespace std;

class Student {
private:
	char* m_name;
	int m_age;
	float m_score;
public:
	Student();  //重载默认构造函数
	Student(char* name, int age, float score);  //重载默认构造函数
	void information2();  //普通成员函数
	void setname(char* name);
	void setage(int age);
	void setscore(float score);
};

//构造函数的重载
Student::Student() {
	m_name = NULL;
	m_age = 0;
	m_score = 0.0;
}
Student::Student(char* name, int age, float score) {
	m_name = name;
	m_age = age;
	m_score = score;
}

//定义普通成员函数
void Student::information2() {
	if (m_name == NULL) {
		cout << "error" << endl;
	}
	else {
		cout << "姓名" << m_name << '\n' << "年龄" << m_age << '\n' << "分数" << m_score << endl;
	}
}
void Student::setname(char* name) {
	m_name = name;
}
void Student::setage(int age) {
	m_age = age;
}
void Student::setscore(float score) {
	m_score = score;
}

int main() {
	//调用构造函数 Student(char *, int, float)
	char name1[9] = "Mr.Right";
	Student students(name1, 15, 92.5f);
	students.information2();

	//调用构造函数 Student()
	Student* pstudents = new Student();
	pstudents->information2();
	pstudents->setname(name1);
	pstudents->setage(16);
	pstudents->setscore(96);
	pstudents->information2();

	return 0;
}

我们还可以用初始化列表给参数赋值,简洁明了。

variable_len_array::variable_len_array(int len):m_len(len){}

以上使用m_len(len)来给m_len赋值。
注意:
(1)初始化列表可以用于全部成员变量,也可以只用于部分成员变量。
(2)成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。
关于第二点,有如下解释:

class Demo{
private:
    int m_a;
    int m_b;
public:
    Demo(int b);
};
Demo::Demo(int b): m_b(b), m_a(m_b){ }

实际上,运行结果是m_a变成随机值,m_b等于b。
理由如下:在初始化列表中,将m_b放在m_a前面,看似是先给m_b赋值,实际上赋值顺序是由他们在类中的声明顺序决定,因此仍然是先给m_a赋值,然而此时m_b还未被赋值,因此给m_a赋了一个奇怪的值。

3.析构函数

析构函数是一种特殊的成员函数,没有返回值,不需要程序员显式调用(无法显示调用),而是在销毁对象时自动执行。
析构函数的名字是在类名前加一个’~’。
析构函数没有参数,因此不能重载。

//模拟变长数组
class variable_len_array{
public:
    variable_len_array(int len);
    ~variable_len_array();  //析构函数
private:
    const int m_len;  //数组长度
    int* m_arr;  //数组指针
};

variable_len_array::variable_len_array(int len):m_len(len){//使用初始化列表给m_len赋值
    if(len > 0){
        m_arr = new int[len];  //此处分配了内存,使用new创建的数据在程序关闭时不会自动删除,需要析构函数。
    }
    else{
        m_arr = NULL;
    }
}
variable_len_array::~variable_len_array(){
    delete[] m_arr;  //释放内存
}

三.静态与常数成员

1. this指针

类中的每一个函数都具有一个隐藏参数:this指针。
this指针是一个指针常量(指向的对象是哪个不能变,其内容可以变),指向当前对象。

variable_len_array::variable_len_array(int len):m_len(len){//使用初始化列表给m_len赋值
    //参数列表实际为(*this, int len)
    if(len > 0){
        //这里的m_arr实际为this->m_arr
        m_arr = new int[len];
    }
    else{
        m_arr = NULL;
    }
}

2.静态成员

(1)静态成员变量

静态成员变量实现了多个对象共享数据的目标。
静态成员变量属于类,而不属于某个具体的对象,用一个类创建的所有对象同时享用静态变量,即所有对象的静态变量保持相同。

class Student{
public:
    Student(char* name, int age, float score);
    void information();
    
    static int m_total;  //静态成员变量
private:
    char* m_name;
    int m_age;
    float m_score;
};

注意,静态成员变量必须在类声明的外部初始化,初始化时不再加static关键字,需要有数据类型。

int Student::m_total = 0;

静态成员变量可以通过对象或类来访问。

//通过类类访问 static 成员变量
Student::m_total = 10;

//通过对象来访问 static 成员变量
char name[9] = "Mr.Right";
Student stu(name, 15, 92.5f);
stu.m_total = 20;

同理也可用动态对象的指针来访问。

(2)静态成员函数

静态成员函数可以直接通过类调用。
静态成员函数没有this指针,无法访问对象的普通成员,只能访问静态成员(包括变量和函数)。

class Student{
public:
    Student(char *name, int age, float score);
public:  //声明静态成员函数
    static int getTotal();
    static float getPoints();
private:
    static int m_total;  //总人数
    static float m_points;  //总成绩
private:
    char *m_name;
    int m_age;
    float m_score;
};

//初始化静态成员变量
int Student::m_total = 0;
float Student::m_points = 0.0;

//定义静态成员函数(不加static),只能访问静态成员
int Student::getTotal(){
    return m_total;
}
float Student::getPoints(){
    return m_points;
}

int main(){
    int total = Student::getTotal();
    float points = Student::getPoints();
}

3.常成员

(1)常成员变量

和普通常量用法相同,声明时加上const。
但注意:
初始化常量成员变量的唯一方法是使用初始化列表。

class variable_len_array{
private:
    const int m_len;
    int *m_arr;
public:
    variable_len_array(int len);
};
//由于m_len是常量成员变量,因此只能用初始化列表给其赋值
variable_len_array::variable_len_array(int len){
    m_len = len;
    m_arr = new int[len];
}

(2)常成员函数

函数定义和声明的时候(两个都要),在函数头部的结尾加上const。

//声明常成员函数
char* getname() const;

//定义常成员函数
char* Student::getname() const{
    return m_name;
}

(3)常对象

const也可用来修饰对象。常对象只能调用类的常成员变量和函数。

四.总结

未完待续。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值