C++类相关概念

1. 函数形参默认值

(1) 建议函数(不仅仅是构造函数)形参默认值只在函数声明中指定;

        (函数声明和定义写在同一个文件中,则函数声明、定义两者之一或两者都可指定形参默认值,两者都指定默认值时,默认值需相同;但函数声明写在头文件、定义写在源文件时,只能在函数声明中指定形参默认值。)

(2)函数参数默认值 “一默到底”;

(3)若在调用函数时传入参数,则相应参数会替换掉默认值;

(4)构造函数只能通过初始化列表对const成员变量进行初始化,不可在其函数体中使用赋值语句进行初始化;

(5)注意:C语言不支持函数形参有默认值的特性。


2. 隐式类型转换和explicit

2.1 构造函数隐式类型转换

class Animal {
public:
	Animal() {   // 无参构造函数
		cout << "Animal()" << endl;
	}

	Animal(int age) :age_(age) {  // 有参构造函数1
		cout << "Animal(int age)" << endl;
	}

	Animal(int age, char gender) :age_(age), gender_(gender) {  // 有参构造函数2
		cout << "Animal(int age, char gender)" << endl;
	}
private:
	int age_;
	char gender_;
};

void test() {
	Animal animal0;               // 无参构造函数
	Animal animal1 = 10;          // 有参构造函数1
	Animal animal2 = { 20,'n' };  // 有参构造函数2
	Animal animal3 = (30, 'n');   // 有参构造函数1,写法错误!!!
}

运行结果:

如上示例,animal1和animal2的构造发生了隐式类型转换。

注意:Animal animal3 = (30, 'n')这种写法错误,因为逗号表达式的值为其中最后一个表达式的值,这里发生窄化转换(不同数值类型的转换),相当于使用n来初始化age,所以调用构造函数Animal(int age)。


2.2 explicit关键字

如何禁止调用构造函数时的隐式类型转换?使用explicit关键字修饰构造函数即可。

使用explicit修饰后,需要显式地指定初始化的类型。

如:

class Animal {
public:
	explicit Animal(int age) :age_(age) { 
		cout << "Animal(int age)" << endl;
	}
private:
	int age_;
	char gender_;
};

void test() {
	Animal animal1 = 10;          // 错误,禁止隐式类型转换。
    Animal animal2 = static_cast<Animal>(10);  // 显式地指定初始化的类型
    Animal animal3 = Animal(10);       // 显式地指定初始化的类型
}

建议:

(1)单参数的构造函数声明为explicit;

(2)explicit只在函数声明时添加。


3. 常量成员函数

3.1 const修饰成员函数体

则该成员函数为“常量成员函数”,不可修改对象中的成员变量。

示例:

class Student {
public:
	Student(int id) :id_(id) {
	}

	auto getId() const {
		id_ += 3;  // 错误。不可修改成员
		return id_;
	}

private:
	int id_;
};

3.2 const修饰返回值为引用的成员函数

则返回的引用不能被修改。

示例:

class Student {
public:
	Student(int id) :id_(id) {
	}

	const auto& getId() const {
		// id_ += 3; 错误。不可修改成员
		return id_;
	}

private:
	int id_;
};

void test01() {
	Student s0(16);
	// s0.getId() += 3; 错误。不可修改返回的引用
	cout << s0.getId() << endl;
}

注意:该返回值为引用的成员函数被两个const修饰,

第一个const表示返回的引用不能被修改,第二个const表示函数体中不能修改对象成员。


3.3  const对象只能调用const成员函数,非const对象可调用const成员函数

示例:

class Student {
public:
	Student(int id, string name) :id_(id),name_(name) {
	}

	int getId() const {    // const成员函数
		return id_;
	}

	string getName() {    // 非const成员函数
		return name_;
	}

private:
	int id_;
	string name_;
};

void test01() {
	const Student s0(16,"ABC");    // const对象
	cout << s0.getId() << endl;    // 正确。const对象可调用const成员函数
	cout << s0.getName() << endl;  // 错误。const对象不可调用非const成员函数

	Student s1(20, "XYZ");         // 非const对象
	cout << s1.getId() << endl;    // 正确。非const对象可调用const成员函数
	cout << s1.getName() << endl;  // 正确。非const对象可调用非const成员函数
}

(1)非const对象可调用const、非const成员函数;

(2)const对象只可调用const成员函数;

(3)const成员函数可被const、非const对象调用;

(4)非const成员函数只可被非const对象调用。


注意:普通函数(非类成员函数)不能用const修饰!

如下错误!

void func() const {  // 错误。普通函数不能用const修饰
}

4. mutable

则const成员函数可修改mutable修饰的成员变量。

class Student {
public:
	Student(int id, string name) :id_(id),name_(name) {
	}

	void setId(int id) const {  // 可修改mutable成员
		id_ = id;
	}

private:
	mutable int id_;
	string name_;
};

5. this指针

(1)普通成员函数隐藏一个this指针,该指针指向调用函数的对象;

(2)this指针只能在普通成员函数中使用,全局函数、static函数不能使用this指针;

(3)非const成员函数,this指针是一个指向非const对象的指针常量;

        (意思是:非const对象只可调用非const成员函数;并且this指针的指向不能改变)


6. static成员

(1)static全局变量:保存在静态区,作用域为当前文件,无法在其他文件使用(使用extern声明也无法使用);

(2)static成员函数只能访问static成员变量;

(3)static成员变量在类内定义,类外初始化;

类外初始化时不必加static关键字。

如下:

class Student {
private:
    int id_;    
    static int a_;       // 类内定义static成员变量
    static void setA();  // 类内定义static成员函数
}


/* 类外初始化static成员变量、函数 */
int Student::a = 10;  // 初始化时不必加static关键字;不给初值则为0.
void Student::setA() {
	a_ = 99;      // 正确,static成员函数可访问static成员变量
	// id_ = 88;  // 错误,static成员函数不可访问非static成员变量
}

 (4)static成员变量或函数属于类,为该类所有对象共享;

访问方式:

class Student {
public :
    static int a;
    static void setA();
};

int Student::a = 10;
void Student::setA() {
	a = 99;
}

void test02() {
    Student s0(20, "AAA");
	Student::a;       // 通过类访问static成员变量
	s0.a;             // 通过对象访问static成员变量
    Student::setA();  // 通过类访问static成员函数
	s0.setA();        // 通过对象访问static成员函数
}

7. 拷贝构造函数

用一个已存在的对象初始化新对象,会调用拷贝构造函数。

(1)拷贝构造函数的第一个形参必须是该类类型的引用,可以没有其他参数;若有其他参数,则其他参数必须有默认值。如:

Student(const Student& s, int age = 18);

(2)拷贝构造函数不建议使用explicit修饰;


8. 隐藏

概念:

派生类中有与基类同名(参数列表可不同)的成员函数,则基类中的该同名成员函数在派生类中不可见(不可调用)。

实例1:派生类中无与基类同名的成员函数。

class Person {
public:
	void run() {
		cout << "Person::run()" << endl;
	}

	void run(int a) {
		cout << "Person::run(int a)" << endl;
	}
};

class Student :public Person {
};

void test() {
    Student s;
	s.run();   // 正常调用
	s.run(3);  // 正常调用
}

示例2:派生类中有与基类同名的成员函数,发生隐藏。

class Person {
public:
	void run() {
		cout << "Person::run()" << endl;
	}

	void run(int a) {
		cout << "Person::run(int a)" << endl;
	}
};

class Student :public Person {
public:
	void run(string s) {
		cout << "Student::run(string s)" << endl;
	}
};

void test() {
	Student s;
	s.run("ABC");    // 正常调用
	s.run();         // 不可调用基类的run()
	s.run(3);        // 不可调用基类的run(int)
}

发生隐藏时,如何调用基类同名的成员函数?

方式1:在派生类的同名成员函数中调用基类的同名成员函数。(遵循访问权限规则)

class Student :public Person {
public:
	void run() {
		Person::run();
	}
};

方式2:C++ 11中using关键字可让基类同名成员函数在派生类中可见。(让基类的同名成员函数在派生类中以重载的方式使用,遵循访问权限规则)

注意: 使用using时只能指定函数名,无需参数列表。

class Person {
public:
	void run() {
		cout << "Person::run()" << endl;
	}

	void run(int a) {
		cout << "Person::run(int a)" << endl;
	}
};

class Student :public Person {
public:
	void run(string s) {
		cout << "Student::run(string s)" << endl;
	}

public:
	using Person::run;  // 只能指定函数名
};

void test() {
	Student s;
	s.run();         // 可调用基类的run()
	s.run(13);       // 可调用基类的run(int)
}

9. 虚函数相关

9.1 override 修饰派生类中的虚函数

建议在派生类虚函数后加关键字override,避免虚函数名、返回值类型、参数列表写错。

/*派生类中的虚函数*/
virtual void func() override {
	cout << "Derive::func()" << endl;
}

9.2 final 修饰基类中的虚函数

final修饰基类中的虚函数,禁止在派生类中重写。

/*基类中的虚函数*/
virtual void func() final {
	cout << "Base::func()" << endl;
}

9.3 建议将基类的析构函数写成虚函数


待补充。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值