0、前言:
这段时间,C++知识的总结有些太概括,有些知识需要细分析,没有时间做,这篇文章就先补充一些知识点的细究总结,再继续学习C++面向对象的基础知识。
1、知识补充:
1.1、浅拷贝和深拷贝:
在 C++ 里,深拷贝和浅拷贝都是处理数据复制时的两种方式,主要区别在于是否真正 “复制” 了数据本身
# include <iostream>
# include <cstring>
using namespace std;
class Per { char * name; int num;
public: Per ( char * na, int nu) ; Per ( const Per& a) ; void get_name ( ) ; void show_ip ( ) ;
} ;
Per:: Per ( char * na, int nu) : name ( na)
{ num = nu;
}
Per:: Per ( const Per& a)
{ name = new char ( strlen ( a. name) + 1 ) ; memcpy ( name, a. name, strlen ( a. name) + 1 ) ;
}
void Per:: get_name ( )
{ cout<< "name:" << name << endl;
}
void Per:: show_ip ( ) { cout << "name_ip:" << ( int * ) name << endl;
} int main ( ) { char a[ ] = "李四" ; Per s1 ( a, 1 ) ; Per s3 ( s1) ; s1. get_name ( ) ; s1. show_ip ( ) ; s3. get_name ( ) ; s3. show_ip ( ) ; }
1.2、在C++的类中“成员初始化列表”只能是构造函数(包括拷贝构造函数)来用:
★★★在 C++ 类中,只有构造函数可以使用参数列表(成员初始化列表)的方式初始化成员变量,其他成员函数(包括普通成员函数、析构函数、静态成员函数等)都不能使用这种语法。
1.3、c++中对内存的划分:
栈、堆、全局/静态存储区、常量存储区、代码存储区在硬件的RAM(内存)中,(少数特殊场景如嵌入式系统的代码可能在 ROM 中 ),通过操作系统对物理内存的逻辑划分,通过 权限控制 (只读 / 读写)、管理方式 (自动 / 手动)、生命周期 (临时 / 持久)来区分。硬件(内存 + CPU 的 MMU【内存管理单元】)负责将这些逻辑地址映射到实际的物理内存单元,并强制执行访问规则(比如写只读区会触发异常)。
1.4、定义类的时候,如果有常量,应该如何写构造函数?
如下面代码所示,如果出现常量Pi,就不要在构造函数的声明当中写了,在构造函数或者拷贝构造函数定义的时候,通过“成员初始化列表”完成对Pi的赋值。
class Point { int x; int y;
public: Point ( int x1 = 0 , int y1 = 0 ) ; Point ( const Point& p) ; int get_x ( ) ; int get_y ( ) ;
} ;
class Circle { const double Pi; Point center; double radius;
public: Circle ( Point center1, double radius) ; Circle ( const Circle& p) ;
} ;
Circle:: Circle ( Point center1, double radius1) : Pi ( 3.14 )
{ center = center1; radius = radius1; cout << "正在构造Circle" << endl;
}
Circle:: Circle ( const Circle& p) : Pi ( p. Pi)
{ center = p. center; radius = p. radius; cout << "正在拷贝构造Circle" << endl;
}
1.3、★★★C++中为什么喜欢用引用传参?
普通形参(值传递):会创建实参的完整副本,消耗与实参同等大小的内存。 引用传参:不会赋值实参数据,仅通过地址绑定原变量,内存开销小(只存储地址),且能够直接操作原变量,尤其是在传递类中对象的时候,引用传递形参相对值传递更好一些。
1.4、类中成员函数参数的默认值写在哪里?
#include <iostream>
using namespace std; class MyClass {
private : int value;
public : // 构造函数声明:在这里指定参数默认值MyClass ( int v = 10) ; // 默认值10写在声明中void show() { cout << "value: " << value << endl; }
} ;// 构造函数定义:不需要再写默认值
MyClass::MyClass(int v) { // 这里不能写成 int v = 10value = v;
}
2、组合类:把类嵌套到另一个类当中
2.1、 实现类的组合的前提条件:
类的属性不仅可以是基本数据类型,也可以是类的对象;
2.2、 组合类中构造函数执行的顺序:
★类中如果有多个内嵌对象,则组合类构造函数的执行顺序如下:如果在类中构造了该内嵌对象,则按照内嵌对象的声明顺序构造;如果在主函数中构造内嵌对象,构造顺序就是主函数中的构造顺序,【C++ 中,当一个类没有定义任何构造函数时,编译器会自动生成一个 “默认构造函数”(无参数、什么都不做)。但如果你手动定义了任何构造函数(哪怕只有一个带参数的),编译器就不会再自动生成默认构造函数了。 】具体案例如下:
# include <iostream>
using namespace std;
class A {
private: int id;
public: A ( int id) : id ( id) { cout << "A" << id << " 被构造" << endl; }
} ;
class B {
private: int id;
public: B ( int id) : id ( id) { cout << "B" << id << " 被构造" << endl; }
} ;
class C {
private: A a; B b;
public: C ( int aId, int bId) : a ( aId) , b ( bId) { cout << "组合类C 被构造" << endl; }
} ;
class D {
private: A a; B b;
public: D ( A aObj, B bObj) : a ( aObj) , b ( bObj) { cout << "组合类D 被构造" << endl; }
} ; int main ( ) { cout << "=== 场景1:内嵌对象在组合类内部构造 ===" << endl; C c ( 1 , 2 ) ; cout << "\n=== 场景2:内嵌对象在主函数中构造 ===" << endl; B b3 ( 3 ) ; A a4 ( 4 ) ; D d ( a4, b3) ; return 0 ;
}
2.3、组合类函数构造函数使用“成员初始化列表”的理由:
★★★组合类中关于构造函数踩过的坑 :类会给定义未初始化的对象调用默认构造函数,把对象赋值给同一个类的另一个对象,就会默认调用拷贝函数;
# include <iostream>
using namespace std;
class A {
private: int id;
public: A ( int a) ; A ( ) ;
} ;
A:: A ( int a) { id = a; cout << "正在构造A:" << id << endl;
}
A:: A ( ) { id = 0 ; cout << "正在构造A:" << id << endl;
}
class B {
private: int b; A a;
public: B ( A a1, int b) ;
} ;
B:: B ( A a1, int b1) { a = a1; b = b1; cout << "正在构造B:" << b << endl;
}
class C {
private: int c; A a2;
public: C ( A a3, int b1) ;
} ;
C:: C ( A a3, int c1) : a2 ( a3) , c ( c1) { cout << "正在构造C:" << c << endl;
} int main ( ) { A a ( 1 ) ; B b ( a, 2 ) ; A a1 ( 3 ) ; C c ( a1, 4 ) ; return 0 ;
}
3、类中的静态成员与静态函数:
3.1、什么是静态成员,什么是静态函数:
静态成员:静态成员可以想象成 “类的共享变量” ,所有对象都共用这一个变量,而不是每个对象各有一份。 静态函数:它是 “类的共享函数 ”,在其访问权限内,通过类和对象都可以调用。
3.2、静态成员和静态函数的特点:
静态成员的特点:1、类中普通成员在每一个对象中都有一个拷贝,存储不同的值,但是静态数据成员是每一个类中只有一个拷贝,这个类创建的所有对象都可以访问这个静态数据成员;书写格式:static 数据类型标识符 成员名; 2、静态成员是在类外进行赋值的,格式:数据类型标识符 类名::静态数据成员名=初始值; 3、在静态成员访问的权限之类,可以通过: 类名::静态数据成员名 访问。 静态函数的特点:1、书写格式:声明时,在成员函数前面加static;2、只能访问静态数据成员和静态成员函数,不能访问非静态数据成员和非静态成员函数 ;3、可以通过:类名::静态函数的方式直接调用;【 ★★★理由:静态函数属于类级别,不绑定到任何具体对象,没有 this 指针(指向对象的指针),因此无法访问 “对象独有的非静态成员”; 而非静态成员和函数依赖 this 指针找到具体对象,所以静态函数不能调用它们。】 ★★★★静态成员和静态函数可以被任何对象访问和修改(只要权限允许,比如 public 修饰),但更重要的是:它们不需要通过对象也能直接访问(通过类名即可)。
3.3、案例演示:
下面的案例,创建学生类,初始化具体学生对象的例子,演示静态数据成员和静态函数成员的特点;
# include <iostream>
using namespace std; class Student { char const * name; int age; int id; static int count;
public: static int a; Student ( char const * na, int ag) ; void get_infor ( ) ; static void get_count ( ) { cout << "现在有:" << count << "人" << endl; }
} ;
Student:: Student ( char const * na, int ag) : name ( na)
{ age = ag; id = count+ 20500 ; count += 1 ;
}
int Student:: count = 0 ;
int Student:: a = 1 ;
void Student:: get_infor ( ) { cout << "姓名:" << name << " 年龄:" << age << " id:" << id << endl;
} int main ( ) { Student:: a; Student s1 ( "s1" , 16 ) ; Student s2 ( "s2" , 17 ) ; Student s3 ( "s3" , 18 ) ; cout << "---------------" << endl; s1. get_infor ( ) ; s2. get_infor ( ) ; s3. get_infor ( ) ; s1. get_count ( ) ; Student:: get_count ( ) ; return 0 ;
}
4、类中的常对象和常成员函数:
4.1、定义
常对象:“被冻结的对象” —— 一旦创建,它的成员变量(普通成员)就不能被修改了,相当于给对象加了一把 “只读锁”;定义方式:const 类名 对象名; 常成员函数:“保证不修改对象数据的函数” —— 这类函数内部不会(也不允许)修改对象的成员变量,是 “安全的只读操作”。(为了能够访问常对象的属性,C++引入的常成员函数)定义方式:函数类型 函数名(形参) const {函数定义}
4.2、特点:
C++禁止使用常对象 来调用普通成员函数(因为普通成员函数有可能改变对象的属性值 ),常对象能不能调用类里面的数据成员关键还得看这个成员的属性,如果是public就可以访问。 常函数 可以访问静态成员和普通数据成员(只能访问不能修改 ),常函数可以访问静态数据成员,并且可以修改,但是常函数不可以访问普通函数(因为普通成员函数有可能改变对象的属性值 ),常函数可以调用静态函数。
4.3、案例演示
# include <iostream>
using namespace std; class Demo {
private: int normalNum;
public: static int staticNum; Demo ( int n) : normalNum ( n) { } void setNormalNum ( int n) { normalNum = n; } static void setStaticNum ( int n) { staticNum = n; } void showInfo ( ) const { cout << "普通私有成员(normalNum): " << normalNum << endl; cout << "修改前静态成员(staticNum): " << staticNum << endl; staticNum++ ; cout << "修改后静态成员(staticNum): " << staticNum << endl; setStaticNum ( 100 ) ; cout << "静态函数修改后(staticNum): " << staticNum << endl; }
} ;
int Demo:: staticNum = 0 ; int main ( ) { Demo obj ( 10 ) ; obj. setNormalNum ( 20 ) ; obj. showInfo ( ) ; cout << "------------------------" << endl; const Demo constObj ( 30 ) ; constObj. showInfo ( ) ; Demo:: setStaticNum ( 200 ) ; cout << "类外访问静态成员: " << Demo:: staticNum << endl; return 0 ;
}
5、类中的this指针:
5.1、定义
引入:c++中的指针 可以理解为 “地址标签”。想象你有很多个抽屉(就像电脑里的内存单元),每个抽屉都有自己的编号(内存地址),抽屉里放着东西(数据)。指针就好比你在纸上写下的某个抽屉的编号。当你想知道那个抽屉里有什么时,不用挨个找,直接看这个编号就能立刻找到对应的抽屉,取出里面的东西。this想象成每个对象自带的 "自我标签。 在方法内部,可以直接用 this->属性 来明确访问当前对象的成员, 定义:简单说,this指针就是让对象在执行方法时,不会搞混 “操作的是自己还是别人”,确保每个对象都能正确处理自己的数据。
5.2、作用:解决参数名冲突时用this只是它的一个常见用法,而它的核心功能是让类的成员函数知道 “自己正在操作哪个具体对象”。
class Person {
private: string name;
public: void setName ( string name) { this-> name = name; }
} ;
# include <iostream>
using namespace std; class Calculator {
private: int result;
public: Calculator ( ) { result = 0 ; } Calculator& add ( int num) { result += num; return * this; } Calculator& multiply ( int num) { result *= num; return * this; } int getResult ( ) { return result; }
} ; int main ( ) { Calculator cal; int res = cal. add ( 5 ) . add ( 3 ) . multiply ( 2 ) . getResult ( ) ; cout << "结果:" << res << endl; return 0 ;
}
# include <iostream>
using namespace std; class Student {
private: int id;
public: Student ( int id) { this-> id = id; } bool isSame ( Student& other) { return this-> id == other. id; }
} ; int main ( ) { Student s1 ( 1001 ) ; Student s2 ( 1001 ) ; Student s3 ( 1002 ) ; cout << "s1和s2是否相同:" << ( s1. isSame ( s2) ? "是" : "否" ) << endl; cout << "s1和s3是否相同:" << ( s1. isSame ( s3) ? "是" : "否" ) << endl; return 0 ;
}
总结:理解 this 的关键是记住:谁调用成员函数,this 就指向谁。
6、类中的友元:
61、定义:
友元(Friend)是 C++ 中一种特殊的访问机制,允许 类外部的函数 或其他类 访问当前类的私有(private)和保护(protected)成员。类通过 “声明友元”,主动向特定的外部元素开放自己的私有成员访问权限 ,就像给这些外部元素 “开了后门”。本质: 友元是类主动授予外部元素(函数或类)访问其私有 / 保护成员的权限,是对封装机制的补充(而非破坏)。
6.2、特点:
书写格式 :friend void externalFunc(MyClass& obj);注意 : 其他类 或者类外函数 如果作为当前类 的友元,必须在当前类 中声明friend,在定义中不用加friend;友元关系是单向的(A 声明 B 为友元,不代表 B 自动把 A 作为友元)。 友元关系不能传递(A 是 B 的友元,B 是 C 的友元,不代表 A 是 C 的友元)。 友元关系不能继承(如果类 A 声明类 B 为友元,那么类 B 的派生类(比如类 C,继承自 B)不会自动成为类 A 的友元。类 C 无法直接访问类 A 的私有 / 保护成员,除非类 A 明确将类 C 也声明为友元;如果类 A 声明类 B 为友元,那么类 B 只能访问类 A 的私有 / 保护成员,不能自动访问类 A 的派生类(如类 C)的私有 / 保护成员,除非类 C 自己明确声明类 B 为友元。) 总结:上面给友元加这么多限制,本身就是对类继承属性的一种保护性补充。 注意: 友元声明不受类访问控制符(public/private/protected)的影响,可以放在类中的任何位置。
6.3、案例演示
# include <iostream>
# include <string>
using namespace std;
class Teacher;
class Student {
private: string name; int score; public: Student ( string n, int s) : name ( n) , score ( s) { } friend void showStudentInfo ( Student& s) ; friend class Teacher;
} ;
class Teacher {
private: string name; public: Teacher ( string n) : name ( n) { } void checkScore ( Student& s) { cout << "教师" << name << "查看学生" << s. name << "的成绩:" << s. score << endl; } void assist ( Student& s) ;
} ;
void showStudentInfo ( Student& s) { cout << "学生信息:" << s. name << ",成绩:" << s. score << endl;
}
void Teacher:: assist ( Student& s) { cout << "教师" << name << "辅导学生" << s. name << endl;
}
void testFriendDirection ( ) { cout << "\n=== 测试友元单向性 ===" << endl; Student s ( "小明" , 85 ) ; Teacher t ( "李老师" ) ; t. checkScore ( s) ;
}
class SubTeacher : public Teacher {
public: SubTeacher ( string n) : Teacher ( n) { } void checkScore ( Student& s) { cout << "代课教师查看学生成绩:" ; cout << "无法直接访问学生成绩(友元不可继承)" << endl; }
} ; void testFriendInheritance ( ) { cout << "\n=== 测试友元不可继承性 ===" << endl; Student s ( "小红" , 90 ) ; SubTeacher st ( "王老师" ) ; st. checkScore ( s) ;
} int main ( ) { Student s1 ( "张三" , 78 ) ; showStudentInfo ( s1) ; Teacher t1 ( "张老师" ) ; t1. checkScore ( s1) ; testFriendDirection ( ) ; testFriendInheritance ( ) ; return 0 ;
}
总结:
1、弄清楚:普通成员,静态成员、静态函数、常函数、普通函数、类,常对象、普通对象他们之间的关系
普通对象的调用权限其实是最大的 ,可以调用常函数、静态函数、静态数据成员及其他普通成员。常对象 可以访问:常函数 、静态函数、静态数据成员,至于能不能访问普通成员,要看这个成员访问权限是不是私有的,私有的(private)不可以,公有的(public)可以,常对象能修改的类中的数据成员只有静态成员。【区分读写权限和访问权限】如果普通对象遇到了同名函数,一个是普通函数,一个是常函数,优先调用普通函数。【这是因为普通对象可以调用这两种函数,但普通函数是更精确的匹配。】 普通函数可以访问普通成员、常函数、静态成员。 常函数可以访问静态成员和有访问权限的 普通数据成员(只能访问不能修改),不可以访问普通函数。 静态函数只能访问静态数据成员和静态成员函数。 【记住:静态只认类,常函数 / 常对象在有访问权限的前提下只认读,普通对象 / 函数最自由。】
2、类当中访问权限和读写权限是两种权限:
访问权限(public/private/protected),读写权限是看有没有const限制。
3、静态函数和常函数关键字位置剖析:
静态函数中static只写在声明里,定义里不用写,因为静态函数修饰的是一个类。常函数中const,定义和声明中都要写,因为const是函数的一部分; static写在函数前面,不会有歧义,const写在函数前面会有歧义,例如const int 函数名,可能导致误认为函数返回值是const int。