diff --git wp-includes/load.php wp-includes/load.php
index 94e6339..09e6067 100644
--- wp-includes/load.php
+++ wp-includes/load.php
@@ -410,7 +410,7 @@ function wp_start_object_cache() {
 
 	if ( function_exists( 'wp_cache_add_global_groups' ) ) {
 		wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'site-transient', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss', 'global-posts' ) );
-		wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
+		wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins', 'post_ancestors' ) );
 	}
 }
 
diff --git wp-includes/nav-menu-template.php wp-includes/nav-menu-template.php
index 51321f5..2173087 100644
--- wp-includes/nav-menu-template.php
+++ wp-includes/nav-menu-template.php
@@ -286,8 +286,6 @@ function _wp_menu_item_classes_by_context( &$menu_items ) {
 				}
 			}
 		}
-	} elseif ( ! empty( $queried_object->post_type ) && is_post_type_hierarchical( $queried_object->post_type ) ) {
-		_get_post_ancestors( $queried_object );
 	} elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
 		$term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
 		$term_to_ancestor = array();
diff --git wp-includes/post-template.php wp-includes/post-template.php
index 0581a10..89ad4c4 100644
--- wp-includes/post-template.php
+++ wp-includes/post-template.php
@@ -1016,8 +1016,7 @@ class Walker_Page extends Walker {
 		$css_class = array('page_item', 'page-item-'.$page->ID);
 		if ( !empty($current_page) ) {
 			$_current_page = get_page( $current_page );
-			_get_post_ancestors($_current_page);
-			if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )
+			if ( in_array( $page->ID, $_current_page->ancestors ) )
 				$css_class[] = 'current_page_ancestor';
 			if ( $page->ID == $current_page )
 				$css_class[] = 'current_page_item';
diff --git wp-includes/post.php wp-includes/post.php
index ea4f3d6..5d1d88d 100644
--- wp-includes/post.php
+++ wp-includes/post.php
@@ -375,53 +375,140 @@ function get_extended($post) {
  * @param int|object $post Post ID or post object.
  * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
  * @param string $filter Optional, default is raw.
- * @return mixed Post data
+ * @return mixed Post data or null on failure
  */
 function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
-	global $wpdb;
 	$null = null;
 
-	if ( empty($post) ) {
-		if ( isset($GLOBALS['post']) )
-			$_post = & $GLOBALS['post'];
-		else
-			return $null;
-	} elseif ( is_object($post) && empty($post->filter) ) {
-		_get_post_ancestors($post);
-		$_post = sanitize_post($post, 'raw');
-		wp_cache_add($post->ID, $_post, 'posts');
-	} elseif ( is_object($post) && 'raw' == $post->filter ) {
+	if ( empty( $post ) && isset( $GLOBALS['post'] ) ) {
+		$_post = & $GLOBALS['post'];
+	} elseif ( is_a( $post, 'WP_Post' ) ) {
 		$_post = $post;
+	} elseif ( is_object( $post ) ) {
+		if ( empty( $post->filter ) ) {
+			$_post = sanitize_post( $post, 'raw' );
+			wp_cache_add( $post->ID, $_post, 'posts' );
+			$_post = new WP_Post( $_post );
+		} elseif ( 'raw' == $post->filter ) {
+			$_post = new WP_Post( $post );
+		} else {
+			$_post = WP_Post::get_instance( $post->ID );
+		}
 	} else {
-		if ( is_object($post) )
-			$post_id = $post->ID;
-		else
-			$post_id = $post;
+		$_post = WP_Post::get_instance( $post );
+	}
+
+	if ( !$_post )
+		return $null;
+
+	$_post->filter = $filter;
+
+	if ( $output == ARRAY_A ) {
+		$__post = $_post->to_array();
+		return $__post;
+	} elseif ( $output == ARRAY_N ) {
+		$__post = array_values( $_post->to_array() );
+		return $__post;
+	}
+
+	return $_post;
+}
+
+/**
+ * WordPress Post class.
+ *
+ * @since 3.5.0
+ *
+ * @property $ID;
+ * @property $post_author;
+ * @property $post_date;
+ * @property $post_date_gmt;
+ * @property $post_content;
+ * @property $post_title;
+ * @property $post_excerpt;
+ * @property $post_status;
+ * @property $comment_status;
+ * @property $ping_status;
+ * @property $post_password;
+ * @property $post_name;
+ * @property $to_ping;
+ * @property $pinged;
+ * @property $post_modified;
+ * @property $post_modified_gmt;
+ * @property $post_content_filtered;
+ * @property $post_parent;
+ * @property $guid;
+ * @property $menu_order;
+ * @property $post_type;
+ * @property $post_mime_type;
+ * @property $comment_count;
+ * @property $ancestors;
+ */
+final class WP_Post {
+
+	private $post;
+
+	public $filter = 'raw';
+
+	public static function get_instance( $post_id ) {
+		global $wpdb;
 
 		$post_id = (int) $post_id;
-		if ( ! $_post = wp_cache_get($post_id, 'posts') ) {
-			$_post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post_id));
+		if ( !$post_id )
+			return false;
+
+		if ( ! $_post = wp_cache_get( $post_id, 'posts' ) ) {
+			$_post = $wpdb->get_row( $wpdb->prepare( "
+				SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1
+			", $post_id ) );
+
 			if ( ! $_post )
-				return $null;
-			_get_post_ancestors($_post);
-			$_post = sanitize_post($_post, 'raw');
-			wp_cache_add($_post->ID, $_post, 'posts');
+				return false;
+
+			$_post = sanitize_post( $_post, 'raw' );
+			wp_cache_add( $_post->ID, $_post, 'posts' );
 		}
+
+		return new WP_Post( $_post );
 	}
 
-	if ($filter != 'raw')
-		$_post = sanitize_post($_post, $filter);
+	public function __construct( $post ) {
+		$this->post = $post;
+	}
 
-	if ( $output == OBJECT ) {
-		return $_post;
-	} elseif ( $output == ARRAY_A ) {
-		$__post = get_object_vars($_post);
-		return $__post;
-	} elseif ( $output == ARRAY_N ) {
-		$__post = array_values(get_object_vars($_post));
-		return $__post;
-	} else {
-		return $_post;
+	public function to_array() {
+		$post = get_object_vars( $this->post );
+		$post['ancestors'] = array();
+		$post['filter'] = $this->filter;
+
+		return $post;
+	}
+
+	public function __isset( $key ) {
+		if ( 'ancestors' == $key )
+			return true;
+
+		return isset( $this->post->$key );
+	}
+
+	public function &__get( $key ) {
+		if ( 'ancestors' == $key )
+			$value = get_post_ancestors( $this );
+		else
+			$value = $this->post->$key;
+
+		if ( $this->filter ) {
+			$value = sanitize_post_field( $key, $value, $this->post->ID, $this->filter );
+		}
+
+		return $value;
+	}
+
+	public function __set( $key, $value ) {
+		if ( 'ancestors' == $key )
+			return;
+
+		$this->post->$key = $value;
 	}
 }
 
@@ -433,16 +520,29 @@ function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
  * @param int|object $post Post ID or post object
  * @return array Ancestor IDs or empty array if none are found.
  */
-function get_post_ancestors($post) {
-	$post = get_post($post);
+function get_post_ancestors( $post ) {
+	if ( !$post )
+		return false;
 
-	if ( ! isset( $post->ancestors ) )
-		_get_post_ancestors( $post );
+	if ( ! $ancestors = wp_cache_get( $post->ID, 'post_ancestors' ) ) {
+		$ancestors = array();
 
-	if ( ! empty( $post->ancestors ) )
-		return $post->ancestors;
+		if ( !empty( $post->post_parent ) && $post->ID != $post->post_parent ) {
+			$id = $ancestors[] = $post->post_parent;
 
-	return array();
+			while ( $ancestor = get_post( $id ) ) {
+				// Loop detection: If the ancestor has been seen before, break.
+				if ( empty( $ancestor->post_parent ) || ( $ancestor->post_parent == $post->ID ) || in_array( $ancestor->post_parent, $ancestors ) )
+					break;
+
+				$id = $ancestors[] = $ancestor->post_parent;
+			}
+		}
+
+		wp_cache_add( $post->ID, $ancestors, 'post_ancestors' );
+	}
+
+	return $ancestors;
 }
 
 /**
@@ -460,16 +560,12 @@ function get_post_ancestors($post) {
  * @param string $field Post field name
  * @param id $post Post ID
  * @param string $context Optional. How to filter the field. Default is display.
- * @return WP_Error|string Value in post field or WP_Error on failure
+ * @return bool|string False on failure or returns the value in post field
  */
 function get_post_field( $field, $post, $context = 'display' ) {
-	$post = (int) $post;
 	$post = get_post( $post );
 
-	if ( is_wp_error($post) )
-		return $post;
-
-	if ( !is_object($post) )
+	if ( !$post )
 		return '';
 
 	if ( !isset($post->$field) )
@@ -3348,13 +3444,8 @@ function get_page_uri($page) {
 		$page = get_page($page);
 	$uri = $page->post_name;
 
-	// A page cannot be it's own parent.
-	if ( $page->post_parent == $page->ID )
-		return $uri;
-
-	while ($page->post_parent != 0) {
-		$page = get_page($page->post_parent);
-		$uri = $page->post_name . "/" . $uri;
+	foreach ( $page->ancestors as $parent ) {
+		$uri = get_page($parent)->post_name . "/" . $uri;
 	}
 
 	return $uri;
@@ -3406,8 +3497,10 @@ function &get_pages($args = '') {
 	$key = md5( serialize( compact(array_keys($defaults)) ) );
 	if ( $cache = wp_cache_get( 'get_pages', 'posts' ) ) {
 		if ( is_array($cache) && isset( $cache[ $key ] ) ) {
-			$pages = apply_filters('get_pages', $cache[ $key ], $r );
-			return $pages;
+			// Convert to WP_Post instances
+			$pages = array_map( 'get_post', $cache[ $key ] );
+
+			return apply_filters( 'get_pages', $pages, $r );
 		}
 	}
 
@@ -3581,6 +3674,9 @@ function &get_pages($args = '') {
 	$cache[ $key ] = $pages;
 	wp_cache_set( 'get_pages', $cache, 'posts' );
 
+	// Convert to WP_Post instances
+	$pages = array_map( 'get_post', $pages );
+
 	$pages = apply_filters('get_pages', $pages, $r);
 
 	return $pages;
@@ -4612,45 +4708,6 @@ function _save_post_hook( $post_id, $post ) {
 }
 
 /**
- * Retrieve post ancestors and append to post ancestors property.
- *
- * Will only retrieve ancestors once, if property is already set, then nothing
- * will be done. If there is not a parent post, or post ID and post parent ID
- * are the same then nothing will be done.
- *
- * The parameter is passed by reference, so nothing needs to be returned. The
- * property will be updated and can be referenced after the function is
- * complete. The post parent will be an ancestor and the parent of the post
- * parent will be an ancestor. There will only be two ancestors at the most.
- *
- * @since 2.5.0
- * @access private
- * @uses $wpdb
- *
- * @param object $_post Post data.
- * @return null When nothing needs to be done.
- */
-function _get_post_ancestors(&$_post) {
-	global $wpdb;
-
-	if ( isset($_post->ancestors) )
-		return;
-
-	$_post->ancestors = array();
-
-	if ( empty($_post->post_parent) || $_post->ID == $_post->post_parent )
-		return;
-
-	$id = $_post->ancestors[] = (int) $_post->post_parent;
-	while ( $ancestor = $wpdb->get_var( $wpdb->prepare("SELECT `post_parent` FROM $wpdb->posts WHERE ID = %d LIMIT 1", $id) ) ) {
-		// Loop detection: If the ancestor has been seen before, break.
-		if ( ( $ancestor == $_post->ID ) || in_array($ancestor,  $_post->ancestors) )
-			break;
-		$id = $_post->ancestors[] = (int) $ancestor;
-	}
-}
-
-/**
  * Determines which fields of posts are to be saved in revisions.
  *
  * Does two things. If passed a post *array*, it will return a post array ready
diff --git wp-includes/query.php wp-includes/query.php
index e976cdf..e8c5454 100644
--- wp-includes/query.php
+++ wp-includes/query.php
@@ -2643,19 +2643,21 @@ class WP_Query {
 
 			if ( $ids ) {
 				$this->set_found_posts( $q, $limits );
-
 				_prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
-
-				$this->posts = array_map( 'get_post', $ids );
+				$this->posts = $ids;
 			} else {
-				$this->found_posts = $this->max_num_pages = 0;
 				$this->posts = array();
+				$this->found_posts = $this->max_num_pages = 0;
 			}
 		} else {
 			$this->posts = $wpdb->get_results( $this->request );
+			update_post_caches( $this->posts, 'any', $q['update_post_term_cache'], $q['update_post_meta_cache'] );
 			$this->set_found_posts( $q, $limits );
 		}
 
+		// Convert to WP_Post objects
+		$this->posts = array_map( 'get_post', $this->posts );
+
 		// Raw results filter. Prior to status checks.
 		if ( !$q['suppress_filters'] )
 			$this->posts = apply_filters_ref_array('posts_results', array( $this->posts, &$this ) );
diff --git wp-includes/taxonomy.php wp-includes/taxonomy.php
index 8ea976f..e3f0a16 100644
--- wp-includes/taxonomy.php
+++ wp-includes/taxonomy.php
@@ -3207,16 +3207,8 @@ function get_ancestors($object_id = 0, $object_type = '') {
 			$ancestors[] = (int) $term->parent;
 			$term = get_term($term->parent, $object_type);
 		}
-	} elseif ( null !== get_post_type_object( $object_type ) ) {
-		$object = get_post($object_id);
-		if ( ! is_wp_error( $object ) && isset( $object->ancestors ) && is_array( $object->ancestors ) )
-			$ancestors = $object->ancestors;
-		else {
-			while ( ! is_wp_error($object) && ! empty( $object->post_parent ) && ! in_array( $object->post_parent, $ancestors ) ) {
-				$ancestors[] = (int) $object->post_parent;
-				$object = get_post($object->post_parent);
-			}
-		}
+	} elseif ( post_type_exists( $object_type ) ) {
+		$ancestors = get_post_ancestors($object_id);
 	}
 
 	return apply_filters('get_ancestors', $ancestors, $object_id, $object_type);
