Home > Articles > Programming > C/C++

An Interview with Bjarne Stroustrup

Danny Kalev talks to Bjarne Stroustrup, C++ creator and author of The C++ Programming Language, fourth edition, about the impact of C++11, why he doesn't consider security to be the job of a programming language, his thoughts on garbage collection, and what's coming in C++14.
Like this article? We recommend

Danny Kalev: Although it may be too early to assess the full impact of C++11 right now, which insights and conclusions have you reached already regarding C++11? Are there new features that you have grown to like more than before? Are there features that need further polishing or fixing?

Bjarne Stroustroup: Adding move semantics is a game changer for resource management. It completes what was started with constructor/destructor pairs, the RAII ("Resource Acquisition Is Initialization") idiom, and smart pointers to enable safe, simple, and efficient handling of general resources. A resource is anything that must be acquired and (explicitly or implicitly) released. Examples are memory, locks, file handles, threads, and sockets.

Consider how to return a potentially large number of elements from a function. Here is a conventional C++98 function returning the indices of string matches:

vector<int>* find_all(vector<string>& vs, const string& s)
{
	vector<int>* res = new vector<int>;
	for (int i = 0; i<vs->size(); ++i)
		if (vs[i] == s)
			res->push_back(i);
	return res;
}
vector<int>* pvi = find_all(some_vec,"vindaloo");
for (vector<int>::iterator p = pvi->begin(); p!=pvi->end(); ++p)
	cout << *p << " is a match\n";
// ...
delete pvi;

Naturally, this would have been more realistic and interesting if I had used a regular expression (std::regex) to find a match.

I used (error-prone) explicit memory management with new and delete. Why? Because using the free store (dynamic memory) is the only way to return a large amount of memory without copying.

Move semantics allows a significant simplification:

vector<int> find_all(vector<string>& vs, const string& s)
{
	vector<int> res;
	for (int i = 0; i<vs.size(); ++i)
		if (vs[i] == s)
			res.push_back(i);
	return res;
}
for (int x : find_all(some_vec,"vindaloo"))
        cout << x << " is a match\n";

I eliminated the explicit memory managements, but I returned the vector by value! Why is that not a bad performance bug? What if there were 100,000 matches?

C++11 supports moves as well as copies. That is, in addition to providing a copy constructor and a copy assignment, a class (such as vector) can offer a move constructor and a move assignment. A copy operation produces an independent copy of the original value and does not modify its source. A move operation is used when the source is no longer needed so that we can “steal” its representation. Consider a simplified vector:

class vector {		// simplified vector of doubles
private:
	double* elem;		// pointer to elements
	int sz;			// number of elements
public:
	vector(const vector& a)			// copy constructor
		:elem{new double{a.size()}, sz{a.size()} {}
	vector& operator=(const vector& a) 	// copy assignment
{ vector tmp = a; swap(*this,tmp); }
			vector(vector&& a)				// move constructor
				:elem{a.elem}, sz{a.size()} { a.elem =nullptr; a.sz = 0;}
			vector& operator=(vector&& a)		// move assignment
				{ delete elem; elem=a.elem; sz=a.sz; a.elem=nullptr; 
a.sz=0; }
	// ...
};

The && is called an rvalue reference and is what identifies the operations as move operations.

A move operation is conceptually very simple. Even babies understand that when we move an object, we don’t first make a copy and then destroy the original! Consider a graphical representation of the call

vector<int> results = find_all(vs,"Greene King");

Before the return from find_all():

After the return from find_all():

The importance of move semantics is that we can basically eliminate complicated and error-prone explicit use of pointers and new/delete in most code. Such direct manipulation of memory can disappear into the implementation of handles, such as vector.

The move semantics is part of my general aim to make simple things simple – without loss of performance, of course. Other features that support that theme is strongly typed constant (including constexpr functions that can be evaluated at compile time), general type aliases (to clean up the syntax of generic code), and uniform and universal initialization (base on the initializer list {} syntax). The code below has examples. For a list of features, see my C++11 FAQ.

Danny: Currently, the committee is working on a new C++ standard dubbed C++14. Can you tell us more about it? Which new features are in line for example?

Bjarne: My definition of the aim of C++14 is “to complete C++11.” That is, to add things we simply didn’t have time to do before shipping C++11 or things that we didn’t quite know how to design without the experience of using C++. It is not the job of C++14 to add major new features or support significant new programming techniques. That’s for C++17. C++14 is a “minor release.”

C++14 will consist of changes to the standard itself and two or more technical reports. The technical reports will be on a file system interface (known as “File System”) and on a way of expressing constraint on template arguments (known as “Concepts Lite”).

My favorite C++14 feature is undoubtedly constraints. Consider the classical sort() example:

	list<string> ls = { “Ms. Piggy”, “Kermit”, “Gonzo” };
	vector<string> vs = { “John”, “Paul”, “George”, “Ringo” }; 

	template<class Iter>
	void sort(Iter p, Iter q); 	// sort the elements in [p,q) using <

	sort(vs.begin(),vs.end());	// fine
	sort(ls.begin(),ls.end());	// disaster

The result of the attempt to sort() the list will be a spectacularly long and confusing list of error messages. Good C++ programmers know that sort() requires a random-access iterator, but the compiler doesn’t read manuals, and I did not actually say what sort() requires of its template argument.

In the committee, we tried to solve this for C++11 with something called “concepts,” but ended up with a complex mess. Fortunately, we backed those “concepts” out before they managed to do harm. For C++14, we can achieve most of what we wanted simply and efficiently. For the example above we can write

template<Random_access_iterator Iter>
void sort(Iter p, Iter q); // sort the elements in [p,q) using <

This will make the error message a simple one-liner. There is a branch of GCC that implements this. Importantly, it has less compile-time overhead than workarounds. The design is primarily by Andrew Sutton, Gabriel Dos Reis, and me. The implementation is by Andrew Sutton with some help from Gabriel Dos Reis. Conceptually, this design has roots in extensive work on what concepts would ideally look like and how the ideal uses would look. For example

The basic notion is that a requirement is simply a compile time Boolean function (a predicate) on a type or a set of types. The example above could equivalently be written

template<typename Iter>
           requires Random_access_iterator<Iter>()
void sort(Iter p, Iter q); // sort the elements in [p,q) using <

where Random_access_iterator is just a constexpr function. Now all the rules for “Concepts Lite” fall out of simple Boolean algebra.

For example, I can define a version of sort() that doesn’t require a caller to use iterators explicitly, we

template<Container Cont>
	requires Random_access_iterator<Iterator<Cont>>()
void sort(Cont& c)
{
	sort(c.begin(),c.end());
}

A Container basically is something with a begin(), end(), and a size(). The Iterator<Cont> is Cont’s iterator type. Now, we can sort() a vector:

sort(vs);

The compiler evaluates the predicate to verify that Container<vector>() is true. Given that, is is trivial to add sorting of lists:

template<Container Cont>
	requires Forward_iterator<Iterator<Cont>>()
void sort(Cont& c)
{
	vector<Value_type<Cont>> v {c.begin(),c.end()};
	sort(v);
	copy(v.begin(),v.end(),c.begin());
}

The simplest and typically the most efficient way to sort a list is to copy it into a vector, sort the vector, and copy it back again. That’s what I did. Value_type<Cont> is (obviously) Cont’s value_type. We can now write:

	sort(ls);

The compiler picks the right sort(). Obviously, it could not pick any other sort() because their predicates (constraints, concepts) failed.

This makes the definition and use of templates much simpler, much more similar to "ordinary" programming, and much more pleasant to work with. Let me emphasize: there is no run-time overhead, the compilation is faster than workarounds, and the language rules are simple. One of my long-term aims is to make simple things simple. We made progress on that with C++11 and we’ll do even better in C++14.

A description of “Concepts Lite” proposal as it was a couple of months ago can be found on the ioscpp site: http://isocpp.org/blog/2013/02/concepts-lite-constraining-templates-with-predicates-andrew-sutton-bjarne-s.

And yes. I do think 2014 is realistic for a committee decision, and implementations of the major C++14 are already available for experimentation.

Danny: Speaking of the future of C++, there have been several proposals to add modules to C++. Can you shed more light on modules? What are they exactly? What are they good for? Where do the modules proposals stand these days?

Bjarne: I can’t say much. Doug Gregor is working on a proposal based on work he has been doing with C and Objective C at Apple, and David Vandevoorde from EDG is also trying to contribute. The work is done as a WG21 “Study Group” (see http://www.isocpp.org/std/the-committee). There is a strong and appropriate focus on allowing a transition from current techniques for industrial-sized systems, as opposed to defining an ideal module construct from first principles.

Danny: Your The C++ Programming Language, 4th edition covers many C++11 features including concurrency, lambdas, constexpr and regular expressions. Is C++11 is easier to teach than say C++98? How is that reflected in your book?

Bjarne: TC++PL4 is supposed to be comprehensive (and is), so I haven’t been able to take much advantage of the opportunities to simplify the presentation. TC++PL4 is a complete rewrite of TC++PL3 and is organized differently, so maybe it is more approachable. It contains a four-chapter “Tour of C++” that covers the complete language and standard library in less than 100 pages (Chapters 2-5). This tour is freely available on the web, so people can judge for themselves. Apart from the tour, the book is conventionally organized in chapters on “The Basics,” “Abstraction Mechanisms,” and “The Standard Library.” The description of the standard library alone is 424 pages – and those pages are dense with useful information.

Of course, there are chapters on concurrency and one on regular expressions. In fact, there is at least one chapter for each major component of the standard library. Similarly, there are sections on lambda expressions and constexpr, as well as for all the new features, such as auto, range-for loops, uniform and universal initialization using {}, move operations, in-class member initializers, etc. There are many new features. However, TC++PL4 is not simply a list of features. I try hard to show how to use the new features in combination. C++11 is a synthesis that supports more elegant and efficient programming styles.

It may be worth it to be explicit: C++11 feel like a new language. You can write more elegant, better performing and more maintainable code in C++11 than you could in C++98. TC++PL4 reflects that, so even though almost every sentence in TC++PL3 is still correct, the book no longer consistently reflects “best practices.” We have learned a lot over the last 13 years.

TC++PL is – as ever – aimed at people who are already programmers. For complete beginners and for people with weak skills in the kinds of programming most commonly done in C++, I recommend Programming: Principles and Practice using C++. That does not cover C++11, but it does take a modern approach to C++ and programming in general.

Danny: At least some of your readers are newcomers who haven’t programmed in C++ before. What is your advice for learning C++ from scratch? I bet you wouldn’t recommend that they learn C first before learning C++, as some experts used to suggest...

Bjarne: I have doubts about the areas of expertise of people who would still suggest the “C first” approach to C++. My textbook, Programming: Principles and Practice using C++ (PPP), is an extensive demonstration of my ideas of how to teach programming and C++ to novices. It has also been used for medium-level programmers and even industrially for experienced programmers coming to C++ from significantly different languages.

Basically, C is not the best subset of C++ to learn first. The “C first” approach forces students to focus on workarounds to compensate for weaknesses and a basic level of language that is too low for much programming. Consider a trivial function to compose an email address:

	string compose(const string& name, const string& domain)
	{
		return name+’@’+domain;
	}

Which can be used like this

       string addr = compose(“bs”,”cs.tamu.edu”);

The C version requires explicit manipulation of characters

	char* compose(const char* name, const char* domain)
	{
		char* res = calloc(strlen(name)+strlen(domain)+2);	// space for strings, ‘@’, and 0
		char* p = strcpy(name,res);
		*p = ‘@’;
		strcpy(domain,p+1);
		return res;
	}

Which can be used like this

char* addr = compose(“bs”,"cs.tamu.edu”);
// ...
free(addr);	// release memory when done

Which version would you rather teach? Which version is easier to use? Did I really get the C version right? Are you sure? Why?

Finally, which version is likely to be the most efficient? Yes, the C++ version because it does not have to count the argument characters and does not have to use the free store (dynamic memory) for short argument strings.

This is not an odd isolated example. I consider it typical. So why do so many teachers insist on the “C first” approach? I suspect that it is mainly because that’s what they have done for decades. Because that’s what the curriculum requires. Because that’s the way the teachers learned it in their youth. Because C is considered simpler than C++. Because the students have to learn C (or the C subset of C++) sooner or later anyway.

None of these reasons hold water. In particular, it is easier to learn to write good C code when you know a bit of C++, so maybe we should switch to a “C++ first” approach to teaching C? A good C++ programmer also knows C. PPP has a chapter on C, and I consider PPP a rather good introduction to C, assuming that you need to know both C and C++.

Danny: In one of our former interviews you expressed reluctance towards using lambda expressions and attributes in new C++ projects. Do you still think that these features (especially lambdas) are best avoided?

Bjarne: Every powerful new feature will be overused until programmers settle on a set of effective techniques and find which uses impede maintenance and performance. I don’t think I ever described lambda expressions as “best avoided,” but – as with all new features – I do encourage a bit of caution. I have already seen multi-page lambdas, but I prefer all non-trivial operations to be named. If you need a lambda of more than an expression or two, name it. A good name indicates intent, and separating definition and use gives the opportunity for a well-chosen comment. Complicated expressions (with or without lambdas) can be a source of errors and a maintenance hazard.

So, lambdas can be used for “write-only code.” They can also be used for terse, efficient, and elegant code. There are many situations where a simple lambda is the obviously more readable solution compared to alternatives. For example:

sort(vd,[](double x, double y) { return abs(x)>abs(y); });

Here, I assume that I have defined a sort() function for a container (as in my answer to question 2).

Given that C++11 supports concurrency, a major use of lambdas is to specify tasks to be executed. For example:

parallel_invoke(	// invoke the three argument tasks in parallel
           [&]{ some_function(a,b,c); }, 
           [&]{ some_other_function(c,d,e); } ,
           [&]{ some_third_function(x,y,e); }      );

This parallel_invoke() is being proposed for C++17. Here, I take advantage of the lambda's ability to access local data (e.g., a, b, and c). These functions better be independent. In particular, c and e should probably be immutable (const).

Danny: I recently interviewed Robert Seacord, a well-known authority on software security. He sounds only partially pleased with how the C and C++ committees address the issue of security. How much emphasis does the C++ standards committee put on making the language more secure? Are there new proposals that are aimed to improve the inherent security of C++?

Bjarne: I do not consider it the job of a programming language to be “secure.” Security is a systems property and a language that is – among other things – a systems programming language cannot provide that by itself. C++ offers protection against errors, rather than protection against deliberate violation of rules. C++11 is better at that than C++98, but the repeated failures of languages that did promise security (e.g. Java), demonstrates that C++’s more modest promises are reasonable. Unfortunately, “we” have built an infrastructure with weak interfaces, poor hardware protection, and a maze of complex communication protocols that seriously complicates security, but that’s not for any individual language or even for all programming languages to solve. Trying to address security problems by having every programmer in every language insert the right run-times checks in the code is expensive and doomed to failure.

Eventually, people will have to rely on specialized tools rather than simply on language features and coding guidelines.

That said, C++ can be used far better than what you get from a novices from a C or Java background. If you use iostreams rather than stdio, you are immune to simple and direct injection attacks. If you use containers (that know when and how to grow to accommodate more elements) rather than arrays, you don’t encounter buffer overflows. If you leave resource management to handles and containers relying on RAII, rather than littering your code with pointer, news and deletes, you don’t encounter resource leaks or write to freed memory.

However, there is no protection against a cast from an integer value to a pointer (as needed in device drivers) or against deliberate access outside the bounds of an array. Nor, of course, is there any protection against someone passing an unchecked string from input to a database interface.

Using a modern style of C++, you can avoid almost all casts and reduce type violations of all kinds to something close to zero. Conversely, if you insist on sticking to a conventional C style of C++ code, you’ll get conventional C-style problems.

Danny: Some 15 years ago I thought that inline would become similar to register – a keyword that compilers can completely ignore because they know better than the average programmer how to optimize code. Have we reached that point already with respect to inline? Are there other C++ features that seem redundant in 2013?

Bjarne: register was made redundant by a simple and general algorithm that cannot be beaten by humans. There still is no such simple and general alternative to inline, and I don’t expect to see one any day soon. I do not know how well the mythical “average programmer” can handle inline, but certainly restrained use of inline can be massively useful.

My own rule of thumb is to use inlining (explicitly or implicitly) only for simple one- or two-line functions that I know to be frequently used and unlikely to change much over the years. Things like the size() function for a vector. The best uses of inlining is for function where the body is less code than the function call and return mechanism, so that the inlined function is not only faster than a non-inlined version, but also more compact in the object core: smaller and faster.

Danny: You've addressed a lot of unfair criticism and myths about C++ before. One of those myths is that one needs Garbage Collection (GC) for reliable software. What’s your take on that? Would it be correct to say that GC has lost its luster, or are there plans to add it to C++ in the future?

Bjarne: I consider GC the last alternative after cleaner, more general, and better localized alternatives to resource management have been exhausted.

GC is fundamentally a global memory management scheme. Clever implementations can compensate, but some of us remember too well when 63 processors of a top-end machine were idle when 1 processor cleaned up the garbage for them all. With clusters, multi-cores, NUMA memories, and multi-level caches, systems are getting more distributed and locality is more important than ever.

Memory is not the only resource. A resource is anything that has to be acquired and (explicitly or implicitly) released after use. Examples are memory, locks, sockets, file handles, and thread handles. A good resource management system handles all kinds of resources. If the release does not happen, we have a leak, and since there is a finite amount of each kind of resource, eventually the system grinds to a halt. You don’t need an actual leak to cause bad effects; excessive resource retention can be almost as bad. For example, if a system holds on to memory, locks, files, etc., for twice as long, the system needs to be provisioned with potentially twice as many resources.

So, I say that C++ is my favorite GC language because it generates so little garbage. C++11 supports a garbage collection interface, so that you can use GC as a last resort (for memory); but before resorting to that, I suggest systematic use of resource handles: Let each resource have an owner in some scope and by default be released at the end of its owner’s scope. This, by the way, is exactly the way Unix handles memory, locks, and files on a per-process basis. In C++, this is known as RAII (“Resource Acquisition Is Initialization”) and is integrated with error handling in the form of exceptions. Resources can be moved from scope to scope using move semantics or “smart pointers,” and shared ownership can be represented by “shared pointers,” shared_ptr, which is a pointer to a shared object that is released (destroyed) when its last shared_ptr goes out of scope (is destroyed).

In the C++11 standard library, RAII is pervasive: for example, memory (string, vector, map, unordered_map, etc.), files (ifstream, ofstream, etc.), threads (thread), locks (lock_guard, unique_lock, etc.), and general objects (through unique_ptr and shared_ptr). The result is implicit resource management that is invisible in common use and leads to low resource retention durations.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020