C++类与对象基本语法(1)

一、概述

1、什么是对象

(1)万物皆对象。
(2)程序就是一组对象,对象之间通过消息交换信息。
(3)类是对对象的描述和抽象,对象是类的具体化和实例化。

2、通过类描述对象

类是从属性和行为两个方面对对象进行抽象的。
属性:姓名、年龄、学号。
行为:吃饭、睡觉、学习。

3、面向对象程序设计(OOP)

(1)精通一种面向对象的元语言 —— UML。
(2)研究设计模式——GOF。


二、类的基本语法

1、类的定义
class 类名{};
1.1、成员变量 —— 属性
class 类名{
	类型 成员变量名;
};
1.2、成员函数 —— 行为
类名{
	返回类型 成员函数名(形参表){
		函数体
	}
};

如:

class Student{
	string m_name;
	int m_age;
	void eat(const string& food){
		...
	}
};

2、访问控制属性

(1)公有成员:public,谁都可以访问。
(2)私有成员:private,只有自己可以访问。
(3)保护成员:protected,只有自己和自己的子类可以访问。
(4)类的成员缺省访控属性为私有,而结构体的成员缺省访控属性为公有。

#include <iostream>
using namespace std;

class Student{
private:
	string m_name;
	int m_age;
public:
	void eat(const string& food){
		cout << m_age << "岁的" << m_name << "同学正在吃" << food <<"。" << endl;
	}
	void setName(const string& name){
		if (name == "2")
			cout << "你才" << name << "!" << endl;
		else
			m_name = name;
	}
	void setAge(int age){
		if (age < 0)
			cout << "无效的年龄!" << endl;
		else
			m_age = age;
	}
};

int main(){
	Student student;
	student.setName("张飞");
	student.setAge(25);
	student.eat("包子");
	return 0;
}

3、构造函数
class 类名{
	...
	类名(形参表){
		构造函数体;
	}
};

当一个对象被创建时,构造函数会自动被执行,其参数来自构造实参。
(1)构造函数可以通过构造参数实现重载。
(2)如果一个类没有定义任何构造函数,那么系统就会缺省的为其提供一个无参构造函数,该构造函数对于基本类型的成员变量不做初始化,对于类类型的成员变量,调用其相应类型的无参构造函数初始化。
(3)对象的创建过程
分配内存 -> 调用构造函数
           -> 调用类类型成员的构造函数 -> 构造函数的代码

#include <iostream>
using namespace std;

class Student{
private:
	string m_name;
	int m_age;
public:
	void eat(const string& food){
		cout << m_age << "岁的" << m_name << "同学正在吃" << food <<"。" << endl;
	}
	/*void _ZN7Student3eatERKSs(Student* this, const string& food){
		cout << this->m_age << "岁的" << this->m_name << "同学正在吃" << food <<"。" << endl;
	}*/

	void setName(const string& name){
		if (name == "2")
			cout << "你才" << name << "!" << endl;
		else
			m_name = name;
	}
	void setAge(int age){
		if (age < 0)
			cout << "无效的年龄!" << endl;
		else
			m_age = age;
	}
	//构造函数
	Student(const string& name, int age){
		m_name = name;
		m_age = age;
	}
	//无参构造
	Student(){
		m_name = "无名";
		m_age = 0;
	}
	//单参构造
	Student(const string& name) : m_name(name), m_age(0){}
};

int main(){
	Student s1("张飞", 25);
	s1.eat("包子");
	//_ZN7Student3eatERKSs(&s1, "包子");
	Student s2 = Student("赵云", 22);
	s2.eat("馒头");
	//_ZN7Student3eatERKSs(&s2, "包子");
	Student s3;
	s3.eat("烧饼");
	Student* s4 = new Student("关羽", 26);//堆对象
	s4->eat("油条");
	delete s4;
	Student& s5 = *new Student();
	s5.eat("面条");
	delete &s5;
	Student sa1[3] = {s1, s2};
	sa1[0].eat("KFC");
	sa1[1].eat("KFC");
	sa1[2].eat("KFC");
	Student* sa2 = new Student[3]{s1, s2};//-std=c++0x
	delete[] sa2;
	Student s6("刘备");
	s6.eat("米饭");
	return 0;
}

4、初始化表
class 类名{
	类名(...) : 初始化表{
		构造函数体;
	}
};
/*
const int x = 100;
x = 100;
int& a = b;
a = b;
*/

(1)如果类中含有常量或引用型的成员变量,必须通过初始化表对其初始化。
(2)成员变量的初始化顺序仅与其被声明的顺序有关,而与初始化表的顺序无关。

class A{
public:
	A(char* psz) : m_str(psz), m_len(m_str.length()){}/*因为m_len先声明,故先初始化,但在初始化
		m_len时,要用到m_str的长度,但m_str现在还不存在,故会出错*/
private:
	size_t m_len;
	string m_str;
};
//正确的做法是在初始化表中减少变量间的相互耦合,这里把m_str.length()改为strlen(psz)。
#include <iostream>
using namespace std;

int g_data;
class A{
private:
	const int m_c;
	int& m_r;
public:
	A() : m_c(100), m_r(g_data){
		//m_c = 100;
		//m_r = g_data;
	}
	void print(void){
		cout << m_c << ' ' << m_r << endl;
	}
};

int main(){
	A a;
	a.print();
	return 0;
}

练习:实现一个时钟,有两种工作模式:一种是计时器;一种是显示当前系统时间。

#include <iostream>
#include <iomanip>
#include <unistd.h>
using namespace std;

class Clock{
private:
	short sec;
	short min;
	short hou;
	
	void show(){
		cout << '\r' << setfill('0') 
			<< setw(2) << hou << ':' 
			<< setw(2) << min << ':' 
			<< setw(2) << sec << flush;
		//printf("\r%02d:%02d:%02d", hou, min, sec);
	}

	void tick(){
		sleep(1);
		if (++sec == 60){
			sec = 0;
			if (++min == 60){
				min = 0;
				if (++hou == 24){
					hou = 0;
				}
			}
		}
	}

public:
	Clock(bool timer = true) : hou(0), min(0), sec(0){
		if (!timer){
			time_t t = time(NULL);
			tm* local = localtime(&t);
			hou = local->tm_hour;
			min = local->tm_min;
			sec = local->tm_sec;
		}
	}

	void run(){
		while (1){
			show();
			tick();
		}
	}
};

int main(){
	Clock clock;
	//Clock clock(false);
	clock.run();
	return 0;
}

5、析构函数
class 类名{
	~类名(void){
		析构函数体;
	}
};

当一个对象被销毁时,自动执行析构函数。局部对象离开作用域时被销毁,堆对象delete时被销毁。
如果一个类没有定义任何析构函数,那么系统会提供一个缺省析构函数。缺省析构函数对基本类型的成员变量什么也不干,对类类型的成员变量,调用相应类型的析构函数。
一般情况下,在析构函数中释放各种动态分配的资源。
构造:基类-》成员-》子类
析构:子类-》成员-》基类

#include <iostream>
using namespace std;

class Double{
public:
	Double(double data) : m_data(new double(data)){
		cout << "构造" << endl;
	}
	~Double(){
		delete m_data;
		cout << "析构" << endl;
	}

	void print() const{
		cout << *m_data << endl;
	}
private:
	double* m_data;
};

int main(){
	{
		Double d1(3.14);
		d1.print();
	}
	Double* d2 = new Double(1.23);
	delete d2;
	cout << "再见!" << endl;
	return 0;
}

6、this指针

(1)一般而言,在类的构造函数或成员函数中,关键字this表示一个指针,对于构造函数而言,this指向正在被构造的对象,对于成员函数而言,this指向调用该函数的对象。
(2)this指针的用途
  A、在类的内部区分成员变量。

#include <iostream>
using namespace std;

class A{
public:
	int data;

	A(int data){
		cout << "构造:" << this << endl;
		this->data = data;
	}

	void foo(void){
		cout << "foo:" << this << endl;
		cout << this->data << endl;
	}
};

int main(){
	A a(100);
	cout << "main:" << &a << endl;
	a.foo();

	A* pa = new A(100);
	cout << "main:" << pa << endl;
	pa->foo();
	return 0;
}

  B、在成员函数中返回调用对象自身。

#include <iostream>
using namespace std;

class Counter{
public:
	Counter() : m_data(0){}
	Counter& inc(){
		++m_data;
		return *this;
	}
	void print(){
		cout << m_data << endl;
	}
private:
	int m_data;
};

int main(){
	Counter c;
	c.inc().inc().inc();
	c.print();//3
	return 0;
}

  C、在成员函数内部通过参数向外界传递调用对象自身,以实现对象间交互。

#include <iostream>
using namespace std;
class Student;

class Teacher{
public:
	void educate(Student* s);

	void reply(const string& answer){
		m_answer = answer;
	}
private:
	string m_answer;
};

class Student{
public:
	void ask(const string& question, Teacher* t){
		cout << "问题:" << question << endl;
		t->reply("不知道。");
	}
};

void Teacher::educate(Student* s){
	s->ask("什么是this指针?", this);
	cout << "答案:" << m_answer << endl;
}

int main(){
	Teacher t;
	Student s;
	t.educate(&s);
	return 0;
}

7、常函数与常对象

(1)如果在一个类的成员函数的参数表后面加上const关键字,那么这个成员函数就被称为常函数,常函数的this指针是一个常指针。在常函数内部无法修改成员变量,除非该变量具有mutable属性。而且在常函数内部也无法调用非常函数。
(2)常对象:拥有const属性的对象、对象引用或指针。

注意:
A、常对象只能调用常函数。
B、同型的常函数和非常函数可以构成重载关系。常对象调用常版本,非常对象调用非常版本。如果没有非常版本,非常对象也可以调用常版本。

const XXX 函数名(const YYY yyy) const{
	...
}	
//第三个const修饰this指针,所以全局函数不能是常函数。
#include <iostream>
using namespace std;

class A{
public:
	void bar(){
		cout << "非常bar" <<endl;
	}
	//void XXXbarYYY(A* this){}
	void bar() const{
		cout << "常bar" << endl;
	}

	void foo() const{
		//m_i = 100;
		const_cast<A*> (this)->m_i = 100;//mutable关键字的作用就是去常,这里自己去常
	}
	/*_ZNK1A3fooEv(const A* this){
		const_cast<A*> (this)->m_i = 100;
	}*/

	void print() const{
		cout << m_i << endl;
	}

	int m_i;
 	//mutable int m_i;
};

int main(){
	A a;
	a.foo();
	a.print();
	const A& r = a;
	r.bar();
	//XXXbarYYY(&r);//const A*
	a.bar();
	//XXXbarYYY(&a);//A*
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值