C++函数名字的隐藏:重写、重载、重定义
c语言函数重载

c语言函数重载C语言是一种广受欢迎的计算机语言,以简单易学及其灵活的特性著称。
它不仅是一种编程语言,还是一种编程范式,用于更好地完成计算机程序的设计和开发。
在计算机程序设计和开发中,函数重载是一个灵活的方法,它可以有效地显示代码的可读性和可扩展性。
本文将介绍C语言函数重载的一般概念,并介绍如何使用它来提高代码可读性和可扩展性。
首先,让我们了解一下什么是函数重载。
函数重载是指在C语言中,允许不同参数引用同一个函数名称,而函数实现不同。
函数名称相同,但参数类型不同,就可以实现函数重载,这样可以提高代码的可读性和可扩展性。
函数重载的主要优势是提高代码的可读性和可扩展性。
由于函数的名称是唯一的,使用者可以更容易地理解函数的功能。
同时,使用者也可以更容易地添加新功能,而不必改变已经存在的函数,提高代码的可扩展性和可维护性。
函数重载还可以提高代码的可重复使用性。
如果两个函数做类似的功能,可以使用函数重载,用一个函数名称,实现不同功能,这样可以大大减少代码量,提高代码的可重复使用性。
函数重载也可以在多个不同的程序中使用,即跨程序函数重载。
函数可以在两个或多个不同的程序中被重载,共享相同的函数名称,这样,可以提高代码的可复用性,以及改进程序可维护性和可扩展性。
函数重载有其局限性。
首先,函数重载只能用于相同参数列表的函数,也就是说,函数重载不能用于不同参数列表的函数。
其次,函数重载只能用于同一个文件中的函数,跨文件的函数重载是不允许的。
最后,只有当参数列表不同时,函数重载才是有效的,如果参数列表相同的函数重载是无效的。
总而言之,C语言函数重载是一种灵活的编程范式,可以显示代码的可读性和可扩展性。
它有很多优势,如提高代码可读性、可重复使用性、可扩展性和可维护性等,但也有一些局限性。
c语言函数重复声明 -回复

c语言函数重复声明-回复C语言函数重复声明在C语言中,函数重复声明是指在程序中多次声明同一个函数的情况。
这往往是由于程序设计不当或编译错误所导致的。
本文将一步一步回答关于C语言函数重复声明的问题,以帮助读者更好地理解和解决这一问题。
1. 什么是函数声明?函数声明是指在代码中告诉编译器有一个函数存在,以便编译器在编译时正确解析该函数的参数、返回值和调用方式。
函数声明一般包括函数的返回类型、函数名和参数类型等信息。
2. 为什么需要函数声明?函数声明的主要目的是为了告知编译器有关函数的信息,以便编译器能够在编译时对函数进行正确的解析和类型检查。
在C语言中,根据C89标准,如果函数未在使用之前进行声明,编译器将隐式假定函数返回类型为int。
这种假设可能导致编译错误或意想不到的结果。
3. 函数重复声明会导致什么问题?函数重复声明会导致编译器错误或警告,因为编译器无法确定应该使用哪个函数声明。
如果函数的定义与其声明不匹配,还可能导致函数行为不正确或程序崩溃。
4. 如何避免函数重复声明?避免函数重复声明的基本原则是只在需要时进行函数声明,并确保每个函数只有一个声明。
以下是一些减少函数重复声明的方法:- 在头文件中定义函数接口:将函数的声明或定义放在头文件中,并在需要使用该函数的源文件中包含头文件。
这样,每个源文件都可以访问函数的声明,而无需重复声明。
- 使用条件编译预处理指令:通过使用条件编译预处理指令(如#ifndef,#define和#endif),可以避免头文件被重复包含,进而导致函数重复声明的问题。
5. 函数声明和函数定义的区别是什么?函数声明是函数的外部接口,在代码中告诉编译器有一个函数的存在,以便编译器能够正确解析和检查函数调用。
函数声明一般包括函数的返回类型、函数名和参数类型等信息,但不包括函数的具体实现。
函数定义是函数的具体实现,包括函数的函数体和实现细节。
函数定义通常包括函数的返回类型、函数名、参数类型、函数体以及返回值等信息。
c语言重定义函数

如何获得新知识英语作文Expanding the Horizons of Knowledge: Strategies for Acquiring New Information.In an era characterized by rapid technological advancements and a deluge of information, the pursuit of knowledge has become increasingly essential for personal growth and societal progress. Acquiring new knowledge empowers us to navigate the complexities of the modern world, make informed decisions, and contribute meaningfully to our communities. However, the sheer volume of information available today can be overwhelming, andfinding effective strategies to filter and absorb knowledge can be a challenge.1. Active Reading and Critical Thinking:Engaging in active reading involves more than simply glancing over a text; it requires actively interrogating the material, questioning its assumptions, and seekingconnections with existing knowledge. Critical thinking skills enable us to analyze, evaluate, and synthesize information, separating facts from opinions and identifying biases. By questioning the author's purpose, evidence, and reasoning, we develop a deeper understanding of the subject matter.2. Immersive Learning Experiences:Immersive learning experiences provide opportunities to engage with knowledge in a tangible and interactive way. These experiences can take various forms, such as attending lectures, participating in workshops, conducting research, or engaging in hands-on activities. By immersing ourselves in the learning environment, we enhance retention andfoster a deeper connection with the material.3. Seek Out Diverse Perspectives:Exposing ourselves to multiple perspectives enriches our understanding by providing us with a broader context and challenging our existing beliefs. Reading from diversesources, including books, articles, podcasts, and online forums, allows us to consider different viewpoints and gain a more comprehensive picture of the topic. Engaging in respectful discussions with individuals from different backgrounds also promotes intellectual growth.4. Leverage Technology for Learning:Technology has opened up numerous avenues for knowledge acquisition. Online learning platforms, educational apps, and virtual reality simulations provide convenient and interactive ways to explore new subjects. These tools often offer personalized learning experiences tailored to individual interests and learning styles, enabling us to learn at our own pace and delve into areas that spark our curiosity.5. Practice Active Recall and Spaced Repetition:Active recall involves regularly testing our knowledge through methods such as flashcards, quizzes, or teaching the material to others. This process strengthens memory byforcing us to retrieve information from long-term storage. Spaced repetition involves reviewing previously learned material at increasing intervals, which helps to solidify knowledge and prevent forgetting.6. Set Learning Goals and Track Progress:Defining specific learning goals provides direction and motivation for knowledge acquisition. By setting clear objectives, we can prioritize our efforts and track our progress. Regular self-assessment helps us identify areas where further learning is needed and provides a sense of accomplishment as we achieve our goals.7. Engage in Meaningful Connections:Connecting new knowledge to existing experiences and knowledge structures helps to make it more personally relevant and memorable. By reflecting on how the new information relates to our personal values, beliefs, and past experiences, we create meaningful associations that enhance retention.8. Foster a Growth Mindset:Adopting a growth mindset, where we embrace challenges and view mistakes as opportunities for learning, is essential for continuous knowledge acquisition. By believing that our abilities can be developed through effort and persistence, we cultivate a lifelong love of learning.9. Find a Knowledge Partner or Mentor:Learning alongside a knowledge partner or mentor can provide valuable guidance and support. Sharing ideas, challenging each other's perspectives, and holding ourselves accountable for our learning progress can accelerate knowledge acquisition and foster a sense of community.10. Engage in Real-World Applications:Applying new knowledge to real-world situations notonly reinforces learning but also provides opportunities for practical implementation. By actively using the information we acquire, we refine our understanding and discover new ways to solve problems or create value.In conclusion, acquiring new knowledge is an ongoing journey that requires an inquisitive mindset, effective strategies, and a commitment to continuous learning. By embracing these practices, we unlock our potential to navigate the complexities of the modern world, make a meaningful impact on our communities, and live fulfilling and intellectually stimulating lives.。
C++类成员函数的重写、重载和隐藏

C++类成员函数的重写、重载和隐藏重载(overload)重载的定义为:在同⼀作⽤域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载。
例如:class A{public: int func(int a); void func(int a, int b); void func(int a, int b, int c); int func(char* pstr, int a);};以上的四个函数均构成重载。
需要注意的是: 1.函数返回值类型与构成重载⽆任何关系 2.类的静态成员函数与普通成员函数可以形成重载 3.函数重载发⽣在同⼀作⽤域,如类成员函数之间的重载、全局函数之间的重载这⾥还需要注意⼀下 const重载:class D{public: void funcA(); //1 void funcA() const; //2 void funcB(int a); //3 void funcB(const int a); //4};在类D 中 funcA 与 const funcA是合法的重载,⽽两个 funcB 函数是⾮法的,不能通过编译。
原因是:顶层const不影响重载性,因为值传递的拷贝特性,想函数内传递的值类型形参的变化不会影响实参,所以有⽆const 对其⽽⾔是没有意义的。
调⽤规则:const对象默认调⽤const成员函数,⾮const对象默认调⽤⾮const成员函数;隐藏(hiding)隐藏定义:指不同作⽤域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。
⽐如派⽣类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。
例如:void hidefunc(char* pstr){ cout << "global function: " << pstr << endl;}class HideA{public: void hidefunc(){ cout << "HideA function" << endl; } void usehidefunc(){ //隐藏外部函数hidefunc,使⽤外部函数时要加作⽤域 hidefunc(); ::hidefunc("lvlv"); }};class HideB : public HideA{public: void hidefunc(){ cout << "HideB function" << endl; } void usehidefunc(){ //隐藏基类函数hidefunc,使⽤外部函数时要加作⽤域 hidefunc(); HideA::hidefunc(); }};隐藏的实质是;在函数查找时,名字查找先于类型检查。
C 的高级特性---函数重载,重写,覆盖

C++语言采用重载机制的另一个理由是:类的构造函数需要重载机制。因为C++规定构造函数与类同名(请参见第9章),构造函数只能有一个名字。如果想用几种不同的方法创建对象该怎么办?别无选择,只能用重载机制来实现。所以类可以有多个同名的构造函数。
8.1.2 重载是如何实现的?
int x = Function ();
则可以判断出Function是第二个函数。问题是在C++/C程序中,我们可以忽略函数的返回值。在这种情况下,编译器和程序员都不知道哪个Function函数被调用。
所以只能靠参数而不能靠返回值类型的不同来区分重载函数。编译器根据参数为每个重载函数产生不同的内部标识符。例如编译器为示例8-1-1中的三个Eat函数产生象_eat_beef、_eat_fish、_eat_chicken之类的内部标识符(不同的编译器可能产生不同风格的内部标识符)。
{
void foo(int x, int y);
… // 其它函数
}或者写成
extern “C”
{
#include “myheader.h”
… // 其它C头文件
}
这就告诉C++编译译器,函数foo是个C连接,应该到库中找名字_foo而不是找_foo_int_int。C++编译器开发商已经对C标准库的头文件作了extern“C”处理,所以我们可以用#include 直接引用这些头文件。注意并不是两个函数的名字相同就能构成重载。全局函数和类的成员函数同名不算重载,因为函数的作用域不同。例如:
示例8-2-1中,函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖。
覆盖(重写),重构,重载的区别

覆盖(重写),重构,重载的区别1:重构:重构, 是对软计设计⽅法的描述, 这个层⾯是架构和设计⽅法,例如有⼀个⽂档⼀开始叫做:names.txt,过⼏天你发现这个⽂档的名字怎么看怎么不顺眼,就是想改名字,这可以叫做是重构。
重构是⼀个软件维护的通⽤技巧, 重构的具体做法, 可以通过重写或者重载等, 但不限于此, 更改注释, 更改函数名类名等,都可以称之为重构,重构的⽬的是为了让软件容易维护, 修改其Bug等.2:重载:重载(Overload)就是通过⽅法的参数识别不同的函数例如我们对构造函数的重载:1package chongzai;23public class Overloads {4private String name;5private int age;6private String loc;7private String sex;8private int cour;910public Overloads(String name, int age, String loc, String sex, int cour) {11super(); = name;13this.age = age;14this.loc = loc;15this.sex = sex;16this.cour = cour;17 }1819public Overloads(String name, int age) {20super(); = name;22this.age = age;23 }2425public Overloads(String name, int age, String loc, String sex) {26super(); = name;28this.age = age;29this.loc = loc;30this.sex = sex;31 }3233 }重载的规则:1、必须具有不同的参数列表;2、可以有不同的返回类型,只要参数列表不同就可以;3、可以有不同的访问修饰符;4、可以抛出不同的异常;5、⽅法能够在⼀个类中或者在⼀个⼦类中被重载。
函数的重载和重写

函数的重载和重写在编写程序时,函数是重要的组成部分之一。
函数可以让代码更具有可读性和可维护性,同时也可以提高代码的复用性。
函数的重载和重写是函数的两种常见形式,本文将对这两种形式进行详细介绍。
函数的重载(Function Overloading)函数的重载是指在同一个作用域内,定义多个同名函数,但是这些函数的参数类型、参数个数或者参数顺序不同。
编译器会根据函数调用时传入的参数类型、个数和顺序来自动选择正确的函数。
函数的重载可以让我们在不改变函数名的前提下,提供更多的函数功能。
例如,我们可以定义一个add函数来实现两个整数相加的功能,同时也可以定义一个add函数来实现两个浮点数相加的功能,这就是函数的重载。
下面是一个简单的add函数的重载示例:```int add(int a, int b) {return a + b;}float add(float a, float b) {return a + b;}```在上面的例子中,我们定义了两个同名的add函数,一个是用于整数相加,一个是用于浮点数相加。
当我们调用add函数时,编译器会自动选择正确的函数来执行。
函数的重写(Function Overriding)函数的重写是指在派生类中重新定义基类中已经定义的函数。
在派生类中重新定义的函数和基类中的函数具有相同的名称和参数列表,但是派生类中的函数实现可以与基类中的函数实现不同。
函数的重写是面向对象编程中的重要概念之一。
通过函数的重写,我们可以实现多态性,即在运行时根据对象的实际类型来调用相应的函数。
下面是一个简单的函数重写的示例:```class Animal {public:virtual void move() {cout << "Animal is moving" << endl;}};class Dog : public Animal {public:void move() {cout << "Dog is running" << endl;}};int main() {Animal* animal = new Dog();animal->move();return 0;}```在上面的例子中,我们定义了一个Animal类和一个Dog类,Dog 类是从Animal类派生而来的。
c++中的函数重载、函数重写、函数重定义

c++中的函数重载、函数重写、函数重定义⽬录为了更加深刻的理解函数重载、重写、重定义,我们可以带着如下这两个问题去思考:1、⼦类中是否可以定义⽗类中的同名成员?为什么? 可以,因为⼦类与⽗类的命名空间不同;2、⼦类中定义的函数是否可以重载⽗类中的同名函数? 不可以,因为函数重载必须在同⼀个作⽤域中。
⼀、函数重载(Function Overloading) 1、什么是函数重载 在同⼀个类中(同⼀个作⽤域中/在类的内部),存在⼀组函数名相同,函数的参数列表不同(参数的个数、类型、顺序),函数有⽆ virtual 关键字都可以,我们把这组函数称为函数重载。
2、为什么使⽤函数重载(函数重载的好处) 由于函数重载可以在同⼀个作⽤域内,使⽤同⼀个函数名命名⼀组功能相似的函数,这样做减少了函数名的数量,避免了程序员因给函数名命名所带来的烦恼,从⽽提⾼程序的开发的效率。
3、函数重载的条件 1. 必须在同⼀作⽤域下 2. 函数名相同但是参数列表不同(参数列表的类型 or 个数 or 顺序不同) 3. 返回值的类型不会影响重载 4. const属性相同4、函数重载的原理(本质:c++编译器对同名函数进⾏重命名) 编译器在编译.cpp⽂件中当前使⽤的作⽤域⾥的同名函数时,根据函数形参的类型和顺序会对函数进⾏重命名(不同的编译器在编译时对函数的重命名标准不⼀样); 但是总的来说,他们都把⽂件中的同⼀个函数名进⾏了重命名;在vs编译器中: 根据返回值类型(不起决定性作⽤)+形参类型和顺序(起决定性作⽤)的规则重命名并记录在map⽂件中。
在linux g++ 编译器中: 根据函数名字的字符数+形参类型和顺序的规则重命名记录在符号表中;从⽽产⽣不同的函数名,当外⾯的函数被调⽤时,便是根据这个记录的结果去寻找符合要求的函数名,进⾏调⽤; 为什么c语⾔不能实现函数重载? 编译器在编译.c⽂件时,只会给函数进⾏简单的重命名; 具体的⽅法是给函数名之前加上”_”;所以加⼊两个函数名相同的函数在编译之后的函数名也照样相同;调⽤者会因为不知道到底调⽤那个⽽出错;1 #include<stdio.h>23int Add(int a, int b)4 {5return a + b;6 }789float Add(float a, float b)10 {11return a + b;12 }1314void testFunc()15 {16 Add(10, 20);17 Add(20.0f, 30.0f);18 }1920int main(int argc, char *argv[])21 {22 testFunc();2325 }案例分析1. 将上述代码保存到.c⽂件中 若上述代码⽤c编译器编译,由于c语⾔中⽆函数重载,所以,在程序运⾏时出错。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
概要:
C++中经常出现函数名字一样,但参数列表或返回值不同的函数,要搞清楚函数的正确调用关系,需理清三个概念:重写(override)、重载(overload)、重定义(redefine)。
一、三个基本概念
1、重定义(redefine):派生类对基类的成员函数重新定义,即派生类定义了某个函数,该函数的名字与基类中的函数名字一样。
特点:(1)不在同一个作用域(分别位于基类、派生类)(2)函数的名字必须相同(3)对函数的返回值、形参列表无要求
特殊情况:若派生类定义的该函数与基类的成员函数完全一样(返回值、形参列表均相同),且基类的该函数为virtual,则属于派生类重写基类的虚函数。
作用效果:若重新定义了基类中的一个重载函数,则在派生类中,基类中该名字的函数(即其他所有重载版本)都被自动隐藏,包括同名的虚函数。
2、重载(overload):函数名字相同,但它的形参个数或者顺序,或者类型不同,但是不能靠返回类型来判断。
特点:(1)位于同一个类中(2)函数的名字必须相同(3)形参列表不同(可能是参数个数 or 类型 or 顺序不同),返回值无要求
特殊情况:若某一个重载版本的函数前面有virtual修饰,则表示它是虚函数。
但它也是属于重载的一个版本
不同的构造函数(无参构造、有参构造、拷贝构造)是重载的应用
作用效果和原理:编译器根据函数不同的参数表,将函数体与函数调用进行早绑定。
重载与多态无关,只是一种语言特性,与面向对象无关。
3、重写(override):派生类重定义基类的虚函数,即会覆盖基类的虚函
数(多态性)
特点:(1)不在同一个作用域(分别位于基类、派生类)(2)函数名、形参列表、返回值相同(3)基类的函数是virtual
特殊情况:若派生类重写的虚函数属于一个重载版本,则该重写的函数会隐藏基类中与虚函数同名的其他函数。
作用效果:父类的指针或引用根据传递给它的子类地址或引用,动态地调用属于子类的该函数。
这个晚绑定过程只对virtual函数起作用
具体原理是由虚函数表(VTABLE)决定的,在第三节介绍。
二、程序实例
1、两个类:基类(取名Test)和派生类(取名XX)名字不规范,哈哈随便取得!
基类和派生类的结构
//Base class
class Test
{
public:
int a;
Test()
{
cout<<"Test() 无参构造函数!"<<endl;
}
Test(int data)
{
a = data;
cout<<"Test(int data) 有参构造函数!"<<endl;
}
Test(const Test &tmp)
{
a = tmp.a;
cout<<"Test 拷贝构造函数!!"<<endl;
}
//基类中对函数名f,进行了重载。
其中最后一个重载函数为虚函数
void f()const
{
cout<<"调用 void Test::f()"<<endl;
}
//overload
int f(int data) const
{
cout<<"调用 Test f(int data)"<<endl;
return1;
}
//overload 虚函数
virtual double f(int dataA,int dataB)
{
cout<<"调用 Test f(int a,int b)"<<endl;
return dataA*dataB/2.0;
}
};
class XX: public Test
{
public:
Test atest;//先调用基类的构造函数,然后对象成员的构造函数,最后才是派生类的构造函数
XX()
{
cout<<"XX() 无参构造函数被调用!"<<endl;
}
//对基类的函数名f,进行了重定义。
则会隐藏基类中的其他f函数
//redefine
int f() const
{
cout<<" 调用 XX f()函数"<<endl;
return1;
}
//重写基类的虚函数
//redefine override
double f(int dataA,int dataB)
{
cout<<"调用 XX f(int dataA,int dataB)函数"<<endl;
return (dataA+dataB)/2.0;
}
};
分析:基类class Test中定义了名为f的3个重载函数,其中最后一个是虚函数
派生类class XX中对f进行了重定义,所以会隐藏基类中名为f的版本。
其中派生类的double f(int dataA,int dataB)属于对虚函数的重写
测试---主程序
int main()
{
//-----test 1------------------------
cout<<"-------test 1------------"<<endl;
//Base class
Test aaTest;
aaTest.f();
aaTest.f(12);
aaTest.f(10,20);
//derived class
XX d;
d.f();
// d.f(2); //error C2661: 'f' : no overloaded function takes 1 parameters
d.f(10,20);
//--------test 2----------------------------------
cout<<"-------test 2------------"<<endl;
Test b = d;
b.f();
b.f(10,20);//调用的是基类的函数,不发生多态
//--------test 3----------------------------------------
cout<<"-------test 3------------"<<endl;
Test &bR = d;//引用
b.f();//f()不是虚函数,调用基类的函数
bR.f(10,20);//调用的是派生类的函数,发生多态
//--------test 4--------------------------------------
cout<<"-------test 4------------"<<endl;
Test* pB = &d;
b.f();
pB->f(10,20);//调用的是派生类的函数,发生多态
return1;
}
分析:(1)test 1中进行了重载测试,根据传递参数的不一样,调用不同的函数(早绑定,与多态无关)
(2)test 2中Test b = d;定义了一个基类对象,用派生类对象来进行初始化。
这会调用基类的拷贝构造函数,生成基类的对象b,基类的拷贝构造函数初始化b的VPTR,指向b的 VTABLE。
因此所有的函数调用都只发生在基类,不会产生多态。
这是一个对象切片过程(参见《C++编程思想.第二版》P370),对象切片是当它拷贝到一个新的对象时,会去掉原来对象的一部分,而不是像使用指针或引用那样简单地改变地址的内容。
(3)test 3和test 4中,定义的基类指针和引用,故会发生多态。
三、晚绑定原理:虚函数表
编译器会对每一个包含虚函数的类(或者从包含虚函数的基类派生的类)创建一个表(VTABLE),里面存放特定类的虚函数的地址。
然后编译器秘密地放置一指针vpointer(VPTR),指向这个对象的vtable。
当通过基类指针做虚函数调用时(即多态调用时),编译器静态地插入能取得这个VPTR并在VTABLE 表中查找函数地址的代码,这样就能调用正确的函数并引起晚绑定的发生。