感谢黑马开源!
一,重载函数
(同一作用域,相同名称,不同参数的函数)
注意事项:
1,(引用/指针)也可以作为重载的条件
传递方式 | 是否可以区分 const ? |
原因 |
---|---|---|
值传递 | ❌ 不能 | const 只影响函数内部,不影响调用时的匹配。 |
引用传递 | ✅ 能 | int& 和 const int& 是不同的类型,可以绑定不同的实参。 |
指针传递 | ✅ 能 | int* 和 const int* 是不同的类型,可以指向不同的数据。 |
关于左值,右值另有高论,参考:深入浅出C++左值与右值 ------ 一个视频彻底搞懂左值与右值!哔哩哔哩bilibili
2,注意默认参数导致的报错
二,类与封装
访问权限相关18
class和struct区别19
默认访问权限,s公有,c私有。
读写控制set/get20
立方体实例21
文件关系22
点和圆关系为例子,将类的声明放在头文件中,将类的实现放在主文件中!
方式 | 优点 | 缺点 |
---|---|---|
#pragma once |
简洁,不易出错 | 非标准(但几乎所有现代编译器都支持) |
#ifndef 宏保护 |
标准 C/C++,兼容性强 | 需要手动确保宏名唯一,代码稍显冗长 |
初识构造函数与析构函数23
构造函数:
1,名同类名,可以有参数,可以重载,对象实例化时自动调用,编译器会在没有设置构造函数时,自动添加一个空构造函数。
2,只会调用一次。
析构函数:
1,~名同类名,无参无构造,不可重载,对象销毁前(出栈)自动调用。编译器会在没有设置析构函数时,自动添加一个空析构函数。
2,只会调用一次,写在main函数中时,会在main函数结束时销毁。
构造函数的分类和调用24
1,按照有无参数分类:有参构造,无参构造
2,按照参数类型分类:普通构造,拷贝构造
拷贝构造传参为常量类对象的引用,可以拷贝出一个和该对象相同的对象。
构造函数的调用方法:
1,括号法
2,显示法
3,隐式转换法
简单写下代码
scss
class Person{
private:
int age;
public:
//constructor
//Parameterless Construction
Person(){
age = 0;
}
//Parametrical Construction
Person(int a){
age = a;
}
//Copy Construction
Person(const Person &p1){
age = p1.age;
}
};
void test01{
//bracket call
Person p;
Person p1(10);
Person p2(p);
//tips:
(X)Person p1();
//In this way , the compiler will evaluates "Person p1()" to a func return type Person.
//display call
Person p3;
Person p4 = Person(10);
Person p5 = Person(p4);
//anonymous object will destroy after this line call.
Person(12);
//tips:
(x)Person(p5);
//We can't initialize anonymous object in Copy Construction.
//Implicit Conversion
Person p6;
Person p7 = 10;
Person P8 = p7;
}
拷贝构造时机25
1,使用一个创建完成的类对象来初始化一个新对象
2,值传递给函数参数传值
3,类对象做返回值(以值方式返回局部对象)
scss
tesr01{
Person p1(10);
Person p2(p1);
}
test02(Person p){
Person p3(p);
}
Person test03(){
//
return Person p;
}
构造函数调用规则26
一个类定义时,系统自动提供三种构造函数。
1,用户自定义有参,系统不自动提供无参
2,用户自定义拷贝,系统不自动提供其他
拷贝构造的浅拷贝与深拷贝27
类默认提供的拷贝构造是浅拷贝,只将类成员变量的值复制给要实例化的对象。在类中有指针成员变量时,浅拷贝只会传递内存地址,在析构函数中会触发堆区内存重复释放的错误,
ini
class Person{
public:
int age;
int *h;
Person(const Person p){
age = p.age;
//deep copy
int h = new int(*p.h);
}
~Person(){
if(h != NULL){
delete h;
}
h = NULL;
}
};
构造函数初始化列表28
arduino
int ca;
int cb;
int cc;
Person(int a,int b,int c) : aa(a),cb(b),cc(c){}
其他类对象做类的成员,构造/析构顺序29
先调用其他类的构造,最后调用其他类的析构
typescript
class Phone{
string pname;
phone (string name):pname(name){}
} ;
class Person{
string hname;
Phone pname;
//Phone pname = phone
//Implicit Conversion
Person(string person, string phone):hname(person),pname(phone){}
}
静态成员函数与静态成员变量30&31
1,静态成员函数和静态成员变量都有访问权限
静态成员变量:
1,所有类对象公用的变量(共用一份数据)
2,在编译阶段分配内存
3,类内声明,类外初始化
对于第三点,我进行了一些尝试,结果是,必须在类外初始化。
如果尝试在定义时初始化,则语法报错提示,如果需要初始化静态成员变量,则该变量必须是常量。
如果只在构造时初始化,则编译后(build error)报错:
LNK2001 unresolved external symbol "public: static int Student::Sch_s"
所以在类外做初始化是必要的!
c
class Student {
public:
static int Sch_s ;
static string Sch_n;
int Xh = 10000;
string stu_name = "Lim";
Student() {
Sch_s++;
}
Student(int xh, string name) :Xh(xh), stu_name(name) {
Sch_s++;
}
static void ChangeSchName(string name){
Sch_n = name;
cout<<"This School change name to "<<name<<endl;
}
void show() {
cout<<"The school name is "<<Sch_n<<endl;
cout << "Name :" << stu_name << " XueHao :" << Xh << endl;
cout << "Now School has " << Sch_s << "Students" << endl;
}
};
int Student::Sch_s = 0;
string Student::Sch_n = "XiWang";
int main() {
Student s1;
Student s2(10001, "miss");
s1.show();
s1.ChangeSchName("GuangMing");
s2.show();
}
静态成员函数:
1,所有类对象共用一个静态成员函数。
2,静态成员函数只能访问静态成员变量。
静态成员函数不能访问非静态成员变量的原因是:
静态成员函数分不清非静态成员变量到底是哪一个对象的变量。
C++对象特性(模型)和this指针之章
成员变量和成员函数分开存储32
通过sizeof检查大小发现,C++编译器会给每个空对象分配一个字节空间,为了区分空对象占用内存的位置。只有类的非静态成员变量在类的对象上,用sizeof可以检查到空间占用。
也就是说,类的非静态成员函数,类的静态成员变量,类的静态成员函数都不在类的对象上。
this指针用途(链式编程思想)33
this指针用途:
1,解决名称冲突
解决形参列表和类成员变量重名的冲突
2,返回对象本身引用
this指针指向被调用的成员函数所属的对象,可以用于在类的非静态成员函数中返回对象本身(引用方式)。
如果在下面的例子中不使用引用的方式,则每次调用add函数只会返回一个对于篇
的拷贝构造,实际上后续的修改没有修改到p2上,所以最终的值为20。
scss
class Person{
int age;
person(int age){
//age = age;
this->age = age;
}
//Person
Person& personAddAge(Person &p){
this->age += p.age;
return *this;
}
};
void test01(){
Person p1(18);
}
void test02(){
Person p1(10);
Person p2(10);
p2.personAddAge(p1).personAddAge(p1).personAddAge(p1);
}
空指针访问类的成员函数34
空类指针(同this指针)可以访问类的成员函数。
类成员函数中使用到类成员变量//时,会自动在前面加上this指针,所以空指针无法取到该成员变量的值会报错。
c
class Person{
int m_age;
showPersonAge(){
//cout<<this->age;
cout<<m_age;
}
};
const修饰成员函数35
this补充:
1,this 指针隐含在每一个非静态成员函数之中
2,this 指针不需要定义,直接使用即可
常函数:
1,成员函数后加const后是常函数
2,常函数内不能修改成员属性
3,成员属性声明时加关键字mutable后,在常函数中可以修改
常对象:
1,声明对象前加const的对象是常对象
2,常对象只能调用常函数
csharp
class Person{
public:
(x)void theEssenceOfThis(){
this = NULL;
}
//Can't change point to(address) also can't change the point to value
//const Person * const this
void constantFunc() const{
//Variables that are not mutable cannot be modified.
//this->m_a = 100;
this->m_b = 100;
}
int m_a;
mutable int m_b;
};
void test01(){
Person p;
//this is Pointer Constant,Can't change point to
//this(Person * const this)
p.theEssenceOfThis();
}
void test02(){
Person p;
p.constantFunc();
}
//constant obj
void test03(){
const Person p;
//p.m_a = 100;
p.m_b = 100;
//constant obj can just call constant func
p.constantFunc();
}
友元36
1,全局函数做友元,可以访问类的私有部分。
csharp
class Building{
//pass by address , avoid the cost of memory
friend void frifunc(Building * building);
public:
Building(){
this->livingroom = "keting";
this->bedroom = "woshi";
}
public:
string livingroom;
private:
string bedroom;
};
void frifunc(Building* building){
cout<<building.livingroom;
cout<<building.bedroom;
}
int main(){
Building b;
frifunc(&b);
return 0;
}
关于类指针的思考
表达式 | 含义 | 地址类型 |
---|---|---|
l1 |
指针存储的地址(指向堆上的 Liter 对象) |
堆地址 |
&l1 |
指针变量 l1 本身的地址(在栈上) |
栈地址 |
l1
是动态分配的对象的地址(堆)。&l1
是指针变量本身的地址(栈)。delete l1
释放堆内存,但不影响l1
的栈地址。
ini
#include<iostream>
using namespace std;
class Liter {
public:
char a, b, c;
Liter(char a, char b, char c) {
this->a = a;
this->b = b;
this->c = c;
}
};
int main() {
Liter *l1 = new Liter('a','b','c');
cout << l1 << endl;
cout << &l1 << endl;
//000002143D4A7EC0
//0000009942EFFB18
delete l1;
l1 = nullptr;
return 0;
}
友元类37
注意,友元关系是一种 "访问授权" ,而不是 "继承授权"。所以没有继承友元类这样的说法。
类做友元,可以访问类的私有部分。
arduino
class Friend{
public:
Friend();
void visit();
Building * building;
}
class Building{
//
friend class Friend;
//pass by address , avoid the cost of memory
friend void frifunc(Building * building);
public:
Building(){
this->livingroom = "keting";
this->bedroom = "woshi";
}
public:
string livingroom;
private:
string bedroom;
};
//member func realize out of class
Friend::Friend(){
building = new Building;
}
void Friend::visit(){
building->livingroom = "ke";
building->bedroom = "wo";
}
成员函数做友元38
arduino
class Building{
friend void Friend::visit();
};
由于超出掘金最大字数限制,我将分p发布。